11

I have a databale on index.xhtml

<h:dataTable style="border: solid 2px black;"
    value="#{IndexBean.bookList}" var="item"
    binding="#{IndexBean.datatableBooks}">

    <h:column>
        <h:commandButton value="Edit" actionListener="#{IndexBean.editBook}">
            <f:param name="index" value="#{IndexBean.datatableBooks.rowIndex}"/>
        </h:commandButton>
    </h:column>
</h:dataTable>

My bean:

@ManagedBean(name="IndexBean")
@ViewScoped
public class IndexBean implements Serializable {
    private HtmlDataTable datatableBooks;

    public HtmlDataTable getDatatableBooks() {
        return datatableBooks;
    }

    public void setDatatableBooks(HtmlDataTable datatableBooks) {
        this.datatableBooks = datatableBooks;
    }

    public void editBook() throws IOException{
        int index = Integer.parseInt(FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("index").toString());
        System.out.println(index);
    }
}

My problem is that I always get the same index in server log even though I click the different edit buttons. Imagine that there is one collection which is supplied to the datatable. I have not shown that in bean.

If I change scope from ViewScope to RequestScope it works fine. What can be the problem with @ViewScoped? Thanks in advance :)

EDIT:

<h:column>
    <h:commandButton value="Edit" actionListener="#{IndexBean.editBook}" />
</h:column>

public void editBook(ActionEvent ev) throws IOException{
    if (ev.getSource() != null && ev.getSource() instanceof HtmlDataTable) {
        HtmlDataTable objHtmlDataTable = (HtmlDataTable) ev.getSource();
        System.out.println(objHtmlDataTable.getRowIndex());
    }
}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
TCM
  • 16,780
  • 43
  • 156
  • 254
  • Related: http://stackoverflow.com/questions/4994458/how-can-i-pass-a-parameter-to-a-commandlink-inside-a-datatable – BalusC Oct 06 '11 at 16:18

3 Answers3

16

You've already bound the <h:dataTable> component to the bean. All you need to do is:

public void editBook() throws IOException{
    int index = datatableBooks.getRowIndex(); // Actually not interesting info.
    Book book = (Book) datatableBooks.getRowData(); // This is what you want.
}

The <f:param> is also not needed here. For more hints also see this article.

Update: I can reproduce your problem. This is likely a bug with @ViewScoped. When the bean is set to @RequestScoped, it works as expected. Also when you remove the component binding and obtain the component from the viewroot yourself, it works as expected. I've filed issue 1658 about this.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 1
    Hi BalusC, your article is great as usual :). Your answer works if my bean is @RequestScoped. But my real question still remains that is, why is it not working in @ViewScoped? If i just change annotation from @RequestScoped to @ViewScoped, i always get the answer that i got 1st time. I need my bean to be @ViewScoped. What workaround can i possibly do? – TCM May 07 '10 at 02:34
  • Where do you load `bookList`? – BalusC May 07 '10 at 11:14
  • I load it in @PostConstruct callback. Am i doing something wrong? Where should i load ? – TCM May 08 '10 at 03:11
  • WOW! That is an excellent article BalusC, everything that we need from a dataTable :) – Narayana Nagireddi Apr 25 '13 at 16:44
  • @Krsna: just add a column with checkboxes/radiobuttons for multiple/single row selection. – BalusC Sep 04 '13 at 11:37
1

What you can do is to use the [getRowData()][1] method on the Java bean to directly get the object located on the line that hosts the button on which the user clicked.

An example of code:

public void editBook(ActionEvent evt) {
    // We get the table object
    HtmlDataTable table = getParentDatatable((UIComponent) evt.getSource());
    // We get the object on the selected line.
    Object o = table.getRowData();
    // Eventually, if you need the index of the line, simply do:
    int index = table.getRowIndex();
    // ...
}

// Method to get the HtmlDataTable.
private HtmlDataTable getParentDatatable(UIComponent compo) {
    if (compo == null) {
        return null;
    }
    if (compo instanceof HtmlDataTable) {
        return (HtmlDataTable) compo;
    }
    return getParentDataTable(compo.getParent());
}


Edit

The JSF code now looks like:

<h:commandButton value="Edit" actionListener="#{IndexBean.editBook}"/>

In addition, do not forget to change the signature of editBook() method, by setting a javax.faces.event.ActionEvent argument.

Romain Linsolas
  • 79,475
  • 49
  • 202
  • 273
  • Hello romaintaz, but i tried your solution and it doesn't seem to work. I keep getting exceptions "SEVERE: Received 'javax.el.MethodNotFoundException' when invoking action listener '#{IndexBean.editBook}' for component 'j_idt35' SEVERE: javax.el.MethodNotFoundException: Method not found: IndexBean@d15176.editBook()" Please help :( – TCM May 06 '10 at 08:19
  • Hello, Please read my edit. I did exactly what you said and i still get exception MethodNotFoundException. I did not understand what you meant by "In addition, do not modify to change the signature of editBook() method, by setting a javax.faces.event.ActionEvent argument." I don't have anything defined in faces-config.xml. As soon as i pass ActionEvent it starts saying MethodNotFoundException – TCM May 06 '10 at 08:38
  • Sorry, I meant "do not *forget* to modify the method signature". Remove the `throws IOException` as it is useless. In addition, the code of your method is wrong in your edit, as the source will not be the `HtmlDatatable` itself. You will have to implement the `getParentDatatable()` method as specified to retrieve the datatable object. – Romain Linsolas May 06 '10 at 08:56
  • Hi romaintaz, The method still doesn't work. It gives MethodNotFoundException. Have you tried it? – TCM May 07 '10 at 02:38
0

If you use EL 2.2, for example with Tomcat7 you can try

<h:commandLink action="#{IndexBean.editBook(item)}" immediate="true">

I hope to help

amanas
  • 490
  • 5
  • 8