How to get selected row index in JSF datatable?
Asked Answered
I

3

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());
    }
}
Ipoh answered 6/5, 2010 at 7:43 Comment(1)
Related: #4994958Goffer
G
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.

Goffer answered 6/5, 2010 at 11:51 Comment(5)
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?Ipoh
Where do you load bookList?Goffer
I load it in @PostConstruct callback. Am i doing something wrong? Where should i load ?Ipoh
WOW! That is an excellent article BalusC, everything that we need from a dataTable :)Capper
@Krsna: just add a column with checkboxes/radiobuttons for multiple/single row selection.Goffer
H
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.

Hardball answered 6/5, 2010 at 8:8 Comment(4)
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: [email protected]()" Please help :(Ipoh
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 MethodNotFoundExceptionIpoh
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.Hardball
Hi romaintaz, The method still doesn't work. It gives MethodNotFoundException. Have you tried it?Ipoh
C
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

Cramoisy answered 6/10, 2011 at 15:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.