Wednesday, June 7, 2006

Using datatables

WARNING - OUTDATED CONTENT!

This article is targeted on JSF 1.2. For JSF 2.0, using datatables can be approached much more elegantly with help of a @ViewScoped managed bean. Please checkout this article which contains two basic CRUD examples, one for JSF 2.0 on Servlet 2.5 and other for JSF 2.0 on Servlet 3.0 which supports passing method arguments in EL 2.2.

Introduction

This whole JSF datatable howto is based on the use of a backing bean with the request scope. You can use it in a session scoped bean, but you have to do some finetuning and those will be explained in the text. It's recommended to use at least JSF RI 1.1_02 or JSF RI 1.2_02 (and thus not 1.x_01 or older; newer is always better, pick the newest if you can), because the 1.x_02 has some important bugfixes. The code examples given below are based on JSF RI 1.2 (Mojarra) with at least Java EE 5.0. At the bottom of this article you can download a WAR containing all examples in Request as well as Session scope using Sun JSF Mojarra 1.2.

Create DTO class

The h:dataTable dynamic tables are nice, you can put any List or DataModel of DTO or Map objects in it. In this article we'll use a List of DTO's.

Imagine a SQL database containing a table with three fields: ID, Name and Value. First create a data wrapper class which represents each single row of the table, this is just a simple class with the table fields (columns) definied as encapsulated private variables which can be accessed by public getters and setters. Such a class is also known as a DTO (Data Transfer Object).

Here is an example called MyData.java:

package mymodel;

public class MyData {

    // Init --------------------------------------------------------------------------------------

    private Long id;
    private String name;
    private String value;

    // Getters -----------------------------------------------------------------------------------

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getValue() {
        return value;
    }

    // Setters -----------------------------------------------------------------------------------

    public void setId(Long id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setValue(String value) {
        this.value = value;
    }

}

It's a good practice to use wrapper datatype objects (Long, Boolean, Integer, etc) instead of primitive datatypes (long, boolean, int, etc) for the properties, because the database fields can contain null values which can not be put into a primitive at all.

Back to top

Retrieve and store data

You can use Hibernate or just plain JDBC to retrieve SQL data. It must return a List of MyData objects. This has to be stored in a private variable dataList which can be accessed by a getter and setter. The setter of the dataList is actually not used by JSF pages (only UIInput values require setters). You can safely remove it if you don't use it somewhere else in the backing bean logic.

The data layer and the DAO pattern is explained in this tutorial: DAO tutorial - the data layer.

Here is the relevant java code for the backing bean. You can load the dataList in either the constructor or initialization block of the bean:

package mypackage;

import java.util.List;

import javax.faces.context.FacesContext;

import mydao.DAOException;
import mydao.DAOFactory;
import mydao.MyDataDAO;
import mymodel.MyData;

public class MyBean {

    // Init --------------------------------------------------------------------------------------

    private MyDataDAO dataDAO = DAOFactory.getInstance("javabase").getMyDataDAO();
    private List<MyData> dataList;

    {
        loadDataList(); // Preload in initialization block.
    }

    // Constructor -------------------------------------------------------------------------------

    public MyBean() {
        loadDataList(); // OR preload in constructor.
    }

    // Actions -----------------------------------------------------------------------------------

    private void loadDataList() {

        // Do your "SELECT * FROM mydata" thing.
        try {
            dataList = dataDAO.list();
        } catch (DAOException e) {
            setErrorMessage(e.getMessage() + " Cause: " + e.getCause());
            e.printStackTrace();
        }
    }

    // Getters -----------------------------------------------------------------------------------

    public List<MyData> getDataList() {
        return dataList;
    }

}

If you're going to use this bean for other purposes than only showing the datalist, then you can better move the loading to the getter so that the data won't be loaded everytime when the bean is constructed. Also use lazy loading in the getter so that the data won't be loaded everytime when the getter is called during the bean's life. Here is an example:


    public List<MyData> getDataList() {
        if (dataList == null) {
            loadDataList(); // Preload by lazy loading.
        }
        return dataList;
    }

Session scope: once loaded the dataList will persist during the session and won't be garbaged after one request in the same session. If you want to reload the session data every view to get the most recent data, then do as follows:


    public List<MyData> getDataList() {
        if (FacesContext.getCurrentInstance().getRenderResponse()) {
            loadDataList(); // Reload to get most recent data.
        }
        return dataList;
    }

The FacesContext#getRenderResponse() returns true when this getter is invoked during the render response phase. This is to prevent duplicate reloads as this getter can also be invoked during the apply request values phase or the process validations phase. Also see Debug JSF lifecycle.

You can also consider to use two beans: one request-scoped bean for the views and form actions and one session-scoped bean for the session data. You can inject the session scoped bean in the request scoped bean as managed property using faces-config.xml. And in the request scoped bean you can invoke reload of the data after save/update/delete action of the session data. Also see Communication in JSF.

Storing the data in the session scope has not only an impact on the server memory usage, but also on the user experience when the data is not for display only. Imagine what would happen with the data when the user manages (edit/delete) the data in multiple browser windows/tabs in the same session.

Instead of using the session scope you can also consider using the Tomahawk t:dataTable component. It has an extra attribute 'preserveDataModel' which you should set to true. This will store the datatable value in the component tree state in session and restore it during the next request. This way you can also avoid unforeseen concurrent updates on the datamodel in between the requests.

Back to top

Show data contents in datatable

First define the backing bean as a request scoped managed bean myBean in the faces-config.xml.

<managed-bean>
    <managed-bean-name>myBean</managed-bean-name>
    <managed-bean-class>mypackage.MyBean</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
</managed-bean>

Session scope: if you want to use the session scope, just change request to session.

Now you can use the h:dataTable to show the contents of the retrieved list.

The relevant JSF code looks like:

<h:dataTable value="#{myBean.dataList}" var="dataItem">
    <h:column>
        <f:facet name="header">
            <h:outputText value="ID" />
        </f:facet>
        <h:outputText value="#{dataItem.id}" />
    </h:column>

    <h:column>
        <f:facet name="header">
            <h:outputText value="Name" />
        </f:facet>
        <h:outputText value="#{dataItem.name}" />
    </h:column>

    <h:column>
        <f:facet name="header">
            <h:outputText value="Value" />
        </f:facet>
        <h:outputText value="#{dataItem.value}" />
    </h:column>
</h:dataTable>

The h:dataTable value should contain the retrieved list from the backing bean definied as managed bean myBean. The h:dataTable var is just a holder for every MyData object of the list. This can be used to access the getters and setters of MyData.java.

The f:facet components are optional. In this case the f:facet name="header" represents the table header (in HTML terms, the <th> element). If you don't need it, you can just leave it away.

Back to top

Add backing bean action to every row

You can use h:commandLink or h:commandButton in one or more columns to invoke a backing bean action and pass the appropriate MyData object to MyBean. This is faster than passing a f:attribute tag with an ID to retrieve the selected item by ID straight from the database. To get the selected row, use the h:dataTable binding to dynamically bind the state of the datatable to the backing bean, so that it knows which row was clicked.

Update the JSP as follows:

<h:form>
    <h:dataTable binding="#{myBean.dataTable}" value="#{myBean.dataList}" var="dataItem">
        <h:column>
            <f:facet name="header">
                <h:outputText value="ID" />
            </f:facet>
            <h:commandLink value="#{dataItem.id}" action="#{myBean.editDataItem}" />
        </h:column>

        ...
    </h:dataTable>
</h:form>

Another way is using f:setPropertyActionListener to set the currently iterated MyData object as a property of MyBean.

<h:form>
    <h:dataTable value="#{myBean.dataList}" var="dataItem">
        <h:column>
            <f:facet name="header">
                <h:outputText value="ID" />
            </f:facet>
            <h:commandLink value="#{dataItem.id}" action="#{myBean.editDataItem}">
                <f:setPropertyActionListener target="#{myBean.dataItem}" value="#{dataItem}" />
            </h:commandLink>
        </h:column>

        ...
    </h:dataTable>
</h:form>
Back to top

Get selected datatable row

Clicking at the h:commandLink of every row will invoke the editDataItem() method of the backing bean. The MyData item belonging to the row can be retrieved using the getRowData() method of the HtmlDataTable class, which is bound by h:dataTable binding. Finally store the MyData item as dataItem.

As we aren't going to make the ID of the MyData item editable, we need to store its value in a HtmlInputHidden component bound to the page so that it can be retained in the next request, even if the validation phase fails.

So extend the backing bean with the following code to retrieve the MyData item of the row selected:

package mypackage;

import javax.faces.component.html.HtmlDataTable;
import javax.faces.component.html.HtmlInputHidden;

import mymodel.MyData;

public class MyBean {

    // Init --------------------------------------------------------------------------------------

    private HtmlDataTable dataTable;
    private MyData dataItem = new MyData();
    private HtmlInputHidden dataItemId = new HtmlInputHidden();

    // Actions -----------------------------------------------------------------------------------

    public String editDataItem() {

        // Get selected MyData item to be edited.
        dataItem = (MyData) dataTable.getRowData();

        // Store the ID of the data item in hidden input element.
        dataItemId.setValue(dataItem.getId());

        return "edit"; // Navigation case.
    }

    // Getters -----------------------------------------------------------------------------------

    public HtmlDataTable getDataTable() {
        return dataTable;
    }

    public MyData getDataItem() {
        return dataItem;
    }

    public HtmlInputHidden getDataItemId() {
        return dataItemId;
    }

    // Setters -----------------------------------------------------------------------------------

    public void setDataTable(HtmlDataTable dataTable) {
        this.dataTable = dataTable;
    }

    public void setDataItem(MyData dataItem) {
        this.dataItem = dataItem;
    }

    public void setDataItemId(HtmlInputHidden dataItemId) {
        this.dataItemId = dataItemId;
    }

}

Session scope: you don't need to pre-instantiate the dataItem with new MyData() as this won't be garbaged after one request. That same way you don't need to retain the ID of the MyData item, so you can safely remove the dataItemId property along with all related logic.

When you're using the f:setPropertyActionListner approach, then the backing bean class should rather look like:

package mypackage;

import javax.faces.component.html.HtmlInputHidden;

import mymodel.MyData;

public class MyBean {

    // Init --------------------------------------------------------------------------------------

    private MyData dataItem;
    private HtmlInputHidden dataItemId = new HtmlInputHidden();

    // Actions -----------------------------------------------------------------------------------

    public String editDataItem() {

        // The dataItem is already set by f:setPropertyActionListener.
        // Store the ID of the data item in hidden input element.
        dataItemId.setValue(dataItem.getId());

        return "edit"; // Navigation case.
    }

    // Getters -----------------------------------------------------------------------------------

    public MyData getDataItem() {
        return dataItem;
    }

    public HtmlInputHidden getDataItemId() {
        return dataItemId;
    }

    // Setters -----------------------------------------------------------------------------------

    public void setDataItem(MyData dataItem) {
        this.dataItem = dataItem;
    }

    public void setDataItemId(HtmlInputHidden dataItemId) {
        this.dataItemId = dataItemId;
    }

}
Back to top

Show selected data item

The contents of the MyData item retrieved can be shown in a new JSP file for edit or just for view. In this example we will show the item for edit.

The relevant JSF code:

<h:form>
    <h:panelGrid columns="2">
        <h:outputText value="ID" />
        <h:outputText value="#{myBean.dataItem.id}" />

        <h:outputText value="Name" />
        <h:inputText value="#{myBean.dataItem.name}" />

        <h:outputText value="Value" />
        <h:inputText value="#{myBean.dataItem.value}" />
    </h:panelGrid>

    <h:inputHidden binding="#{myBean.dataItemId}" />
    <h:commandButton value="Save" action="#{myBean.saveDataItem}" />
</h:form>

Session scope: as mentioned in the previous chapter you don't need the h:inputHidden element, so just keep it away.

Back to top

Save edited data item

Saving is simple, the JSF lifecycle already has updated the MyData item for you. Just do your DAO update thing.

The relevant java code of the backing bean:

package mypackage;

public class MyBean {

    // Actions -----------------------------------------------------------------------------------

    public String saveDataItem() {

        // Retain the ID of the data item from hidden input element.
        dataItem.setId(Long.valueOf(dataItemId.getValue().toString()));

        // Do your "UPDATE mydata SET values WHERE id" thing.
        try {
            dataDAO.save(dataItem);
        } catch (DAOException e) {
            setErrorMessage(e.getMessage() + " Cause: " + e.getCause());
            e.printStackTrace();
        }

        return "list"; // Navigation case.
    }

}

Session scope: the hidden input element is not needed, so you can safely remove the related code lines.

Back to top

Editable datatable

You can also use the datatable to mass-edit the datalist. Also here saving the edited datalist is simple, the JSF lifecycle already has updated the dataList for you. You just need to do your DAO update thing.

The relevant JSF code looks like:

<h:form>
    <h:dataTable value="#{myBean.dataList}" var="dataItem">
        <h:column>
            <f:facet name="header">
                <h:outputText value="ID" />
            </f:facet>
            <h:outputText value="#{dataItem.id}" />
        </h:column>

        <h:column>
            <f:facet name="header">
                <h:outputText value="Name" />
            </f:facet>
            <h:inputText value="#{dataItem.name}" />
        </h:column>

        <h:column>
            <f:facet name="header">
                <h:outputText value="Value" />
            </f:facet>
            <h:inputText value="#{dataItem.value}" />
        </h:column>
    </h:dataTable>
    <br/>
    <h:commandButton value="Save" action="#{myBean.saveDataList}" />
</h:form>

The relevant java code of the backing bean:

package mypackage;

public class MyBean {

    // Actions -----------------------------------------------------------------------------------

    public String saveDataList() {

        // Do your "UPDATE mydata SET values WHERE id" thing for each data item.
        try {
            dataDAO.save(dataList);
        } catch (DAOException e) {
            setErrorMessage(e.getMessage() + " Cause: " + e.getCause());
            e.printStackTrace();
        }

        return "list"; // Navigation case.
    }

}
Back to top

Add new rows to datatable

Adding new rows to the datatable is in fact easy, just add a new and empty MyData item to the dataList and let the rendered attribute of the components intercept on the ID being null. Also add a counter to inform the bean how many rows are been added so that it knows how to prepare the list and how many of the last rows have to be saved.

But in the request scope you'll have to store the counter in a HtmlInputHidden component bound to the page so that its value can be retained in the next request, regardless the outcome of the validations phase.

The relevant JSF code looks like:

<h:form>
    <h:dataTable value="#{myBean.dataList}" var="dataItem">
        <h:column>
            <f:facet name="header">
                <h:outputText value="ID" />
            </f:facet>
            <h:outputText value="#{dataItem.id}" rendered="#{dataItem.id != null}" />
            <h:outputText value="new" rendered="#{dataItem.id == null}" />
        </h:column>

        <h:column>
            <f:facet name="header">
                <h:outputText value="Name" />
            </f:facet>
            <h:outputText value="#{dataItem.name}" rendered="#{dataItem.id != null}" />
            <h:inputText value="#{dataItem.name}" rendered="#{dataItem.id == null}" />
        </h:column>

        <h:column>
            <f:facet name="header">
                <h:outputText value="Value" />
            </f:facet>
            <h:outputText value="#{dataItem.value}" rendered="#{dataItem.id != null}" />
            <h:inputText value="#{dataItem.value}" rendered="#{dataItem.id == null}" />
        </h:column>
    </h:dataTable>
    <br/>
    <h:inputHidden binding="#{myBean.addCount}" converter="javax.faces.Integer" />
    <h:commandButton value="Add" action="#{myBean.addNewDataItem}" />
    <h:commandButton value="Save" action="#{myBean.saveNewDataItems}" />
</h:form>

The relevant java code of the backing bean (note that you should preload the dataList by lazy loading in the getter and that the loadDataList() method is updated):

package mypackage;

import javax.faces.component.html.HtmlInputHidden;

public class MyBean {

    // Init --------------------------------------------------------------------------------------

    private HtmlInputHidden addCount = new HtmlInputHidden();

    // Constructors ------------------------------------------------------------------------------

    public MyBean() {
        // Preset it to 0 to avoid NullPointerExceptions.
        addCount.setValue(0);
    }
    
    // Actions -----------------------------------------------------------------------------------

    private void loadDataList() {

        // Do your "SELECT * FROM mydata" thing.
        try {
            dataList = dataDAO.list();
        } catch (DAOException e) {
            setErrorMessage(e.getMessage() + " Cause: " + e.getCause());
            e.printStackTrace();
        }

        // If any, prepare the list with the new added items.
        for (int i = 0; i < (Integer) addCount.getValue(); i++) {
            dataList.add(new MyData());
        }
    }

    public String addNewDataItem() {

        // Add new MyData item to the data list.
        dataList.add(new MyData());
        addCount.setValue(((Integer) addCount.getValue()) + 1);

        return null; // Postback to same view. You can declare method void as well.
    }

    public String saveNewDataItems() {

        // Do your "UPDATE mydata SET values WHERE id" thing for each new data item.
        try {
            int size = dataList.size();
            dataDAO.save(dataList.subList(size - ((Integer) addCount.getValue()), size));
        } catch (DAOException e) {
            setErrorMessage(e.getMessage() + " Cause: " + e.getCause());
            e.printStackTrace();
        }

        // Reset the amount of newly added items.
        addCount.setValue(0);

        return "list"; // Navigation case.
    }

    // Getters -----------------------------------------------------------------------------------

    public List<MyData> getDataList() {
        if (dataList == null) {
            loadDataList(); // Preload by lazy loading.
        }
        return dataList;
    }

    public HtmlInputHidden getAddCount() {
        return addCount;
    }

    // Setters -----------------------------------------------------------------------------------

    public void setAddCount(HtmlInputHidden addCount) {
        this.addCount = addCount;
    }

}

Session scope: as you don't need to retain the amount of newly added items in the next request -because the complete list already been kept in the session-, you can just remove the addCount property and its getter and setter. Finally replace the property by a simple int property:


    private int addCount;

Update the code in the action methods accordingly. Use it to increment the value in the addNewDataItem() method, to get the sublist in the saveNewDataItems() method and set its value to 0. No getter and setter is needed. Also the h:inputHidden in the JSF page is not needed.

If you're using the FacesContext.getCurrentInstance().getRenderResponse() trick in the getDataList(), then realize that it would override the newly added data items. So remove it or change the code accordingly that it will only reload the datalist in the action methods, e.g. after the save action.

Back to top

Select multiple rows

You also can select multiple rows using checkboxes. There are in general two ways to achieve this: one by adding a boolean property to the DTO and another by adding a Map<SomeIDType, Boolean> to the backing bean.

Here is the first way: just add a boolean property to the MyData.java DTO class and use h:selectBooleanCheckbox to add a checkbox column which triggers this property.

package mypackage;

public class MyData {

    // Init --------------------------------------------------------------------------------------

    private boolean selected;

    // Getters -----------------------------------------------------------------------------------

    public boolean isSelected() {
        return selected;
    }

    // Setters -----------------------------------------------------------------------------------

    public void setSelected(boolean selected) {
        this.selected = selected;
    }

}

Extend the datatable with a checkbox column and add a commandbutton to the form:

<h:form>
    <h:dataTable value="#{myBean.dataList}" var="dataItem">
        <h:column>
            <f:facet name="header">
                <h:outputText value="Select" />
            </f:facet>
            <h:selectBooleanCheckbox value="#{dataItem.selected}" />
        </h:column>

        ...
    </h:dataTable>

    <h:commandButton value="Get selected items" action="#{myBean.getSelectedItems}" />
</h:form>

Finally add the commandbutton action to the backing bean to get the selected items:

package mypackage;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class MyBean {

    // Init --------------------------------------------------------------------------------------

    private List<MyData> selectedDataList;

    // Actions -----------------------------------------------------------------------------------

    public String getSelectedItems() {

        // Get selected items.
        selectedDataList = new ArrayList<MyData>();
        for (MyData dataItem : dataList) {
            if (dataItem.isSelected()) {
                selectedDataList.add(dataItem);
                dataItem.setSelected(false); // Reset.
            }
        }

        // Do your thing with the MyData items in List selectedDataList.

        return "selected"; // Navigation case.
    }

    // Getters -----------------------------------------------------------------------------------

    public List<MyData> getSelectedDataList() {
        return selectedDataList;
    }

}

Here is the another way. If you do not want to extend the MyData object with the boolean property selected for some reasons, then you can also consider the following approach with a Map<SomeIDType, Boolean> where the SomeIDType must represent the same type as the unique ID of the DAO, in this case Long:

<h:form>
    <h:dataTable value="#{myBean.dataList}" var="dataItem">
        <h:column>
            <f:facet name="header">
                <h:outputText value="Select" />
            </f:facet>
            <h:selectBooleanCheckbox value="#{myBean.selectedIds[dataItem.id]}" />
        </h:column>

        ...
    </h:dataTable>

    <h:commandButton value="Get selected items" action="#{myBean.getSelectedItems}" />
</h:form>

The appropriate backing bean code:

package mypackage;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class MyBean {

    // Init --------------------------------------------------------------------------------------

    private Map<Long, Boolean> selectedIds = new HashMap<Long, Boolean>();
    private List<MyData> selectedDataList;

    // Actions -----------------------------------------------------------------------------------

    public String getSelectedItems() {

        // Get selected items.
        selectedDataList = new ArrayList<MyData>();
        for (MyData dataItem : dataList) {
            if (selectedIds.get(dataItem.getId()).booleanValue()) {
                selectedDataList.add(dataItem);
                selectedIds.remove(dataItem.getId()); // Reset.
            }
        }

        // Do your thing with the MyData items in List selectedDataList.

        return "selected"; // Navigation case.
    }

    // Getters -----------------------------------------------------------------------------------

    public Map<Long, Boolean> getSelectedIds() {
        return selectedIds;
    }

    public List<MyData> getSelectedDataList() {
        return selectedDataList;
    }

}
Back to top

Select row by radio button

Using h:selectOneRadio in a JSF datatable is a bit tricky. When putting just a h:selectOneRadio component in a h:column column, the radio buttons gets rendered, but they are not grouped together. Each radio button is attached to one row object and not to the datatable itself, resulting the other radiobuttons not getting unselected when you select one radiobutton. To get it work, use JavaScript to unselect all other radio buttons when one radio button is selected. This is only good for the human eye.

The real work is done by the valuechangelistener attribute of the h:selectOneRadio which fires a ValueChangeEvent. When you clicks a radio button inside a row and then clicks the command button outside the row, the row index of the selected row will be lost. The fired ValueChangeEvent is processed during the third phase of the JSF lifecycle, where the row index is still available. The row index is reset during the fourth phase and the command button action is invoked during the fifth phase. So the trick is to store the object of the selected row during the processing of the ValueChangeEvent and finally handle with it during the command button action.

Add a h:selectOneRadio column to the datatable and supply a fake selectItem to it:

<h:form>
    <h:dataTable binding="#{myBean.dataTable}" value="#{myBean.dataList}" var="dataItem">
        <h:column>
            <f:facet name="header">
                <h:outputText value="Select" />
            </f:facet>
            <h:selectOneRadio valueChangeListener="#{myBean.setSelectedItem}"
                onclick="dataTableSelectOneRadio(this);">
                <f:selectItem itemValue="null" />
            </h:selectOneRadio>
        </h:column>

        ...
    </h:dataTable>

    <h:commandButton value="Get selected item" action="#{myBean.getSelectedItem}" />
</h:form>

Add the valuechangelistener and the commandbutton action to the backing bean to get the selected item and to handle with it:

package mypackage;

import javax.faces.event.ValueChangeEvent;

public class MyBean {

    // Actions -----------------------------------------------------------------------------------

    public void setSelectedItem(ValueChangeEvent event) {

        // Catch the MyData item during the third phase of the JSF lifecycle.
        dataItem = (MyData) dataTable.getRowData();
    }

    public String getSelectedItem() {

        // The MyData item is already catched in setSelectedItem().
        selectedDataList = new ArrayList<MyData>();
        selectedDataList.add(dataItem);

        // Do your thing with the MyData item in List selectedDataList.

        return "selected"; // Navigation case.
    }

}

Here is the JavaScript function which resets all other radio buttons of the same column.

function dataTableSelectOneRadio(radio) {
    var id = radio.name.substring(radio.name.lastIndexOf(':'));
    var el = radio.form.elements;
    for (var i = 0; i < el.length; i++) {
        if (el[i].name.substring(el[i].name.lastIndexOf(':')) == id) {
            el[i].checked = false;
        }
    }
    radio.checked = true;
}
Back to top

Sorting datatable

Update: there's a newer article out for more effective sorting at DAO level. You may find it useful: Effective datatable paging and sorting.

As datatables can be filled with List objects, you can easily sort a datatable using Collections.sort(). The best approach is to add a h:commandLink tag with one f:attribute tag to each column header. The h:commandLink tag should invoke the sorting in the backing bean. The f:attribute tag should pass the column name/type, or in this case, the name of the field getter of the DTO class. And it should be nice to reverse the sorting when clicking again at the same column header.

Add commandlinks with params to the column headers of the datatable:

<h:form>
    <h:dataTable binding="#{myBean.dataTable}" value="#{myBean.dataList}" var="dataItem">
        <h:column>
            <f:facet name="header">
                <h:commandLink actionListener="#{myBean.sortDataList}">
                    <f:attribute name="sortField" value="getId" />
                    <h:outputText value="ID" />
                </h:commandLink>
            </f:facet>
            <h:outputText value="#{dataItem.id}" />
        </h:column>

        <h:column>
            <f:facet name="header">
                <h:commandLink actionListener="#{myBean.sortDataList}">
                    <f:attribute name="sortField" value="getName" />
                    <h:outputText value="Name" />
                </h:commandLink>
            </f:facet>
            <h:outputText value="#{dataItem.name}" />
        </h:column>

        <h:column>
            <f:facet name="header">
                <h:commandLink actionListener="#{myBean.sortDataList}">
                    <f:attribute name="sortField" value="getValue" />
                    <h:outputText value="Value" />
                </h:commandLink>
            </f:facet>
            <h:outputText value="#{dataItem.value}" />
        </h:column>
    </h:dataTable>

    <h:inputHidden value="#{myBean.sortField}" />
    <h:inputHidden value="#{myBean.sortAscending}" />
</h:form>

Session scope: you don't need the h:inputHidden elements as the current sortField and sortAscending won't be garbaged after one request.

Now extend the backing bean with sortDataList() to set the sort field and the sort order. Use the f:attribute value to indicate which MyData field should be sorted. The DTOComparator is described later.

package mypackage;

import java.util.Collections;

import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

import myutil.DTOComparator;

public class MyBean {

    // Init --------------------------------------------------------------------------------------

    private String sortField = null;
    private boolean sortAscending = true;

    // Actions -----------------------------------------------------------------------------------

    public void sortDataList(ActionEvent event) {
        String sortFieldAttribute = getAttribute(event, "sortField");

        // Get and set sort field and sort order.
        if (sortField != null && sortField.equals(sortFieldAttribute)) {
            sortAscending = !sortAscending;
        } else {
            sortField = sortFieldAttribute;
            sortAscending = true;
        }

        // Sort results.
        if (sortField != null) {
            Collections.sort(dataList, new DTOComparator(sortField, sortAscending));
        }
    }

    // Getters -----------------------------------------------------------------------------------

    public String getSortField() {
        return sortField;
    }

    public boolean getSortAscending() {
        return sortAscending;
    }

    // Setters -----------------------------------------------------------------------------------

    public void setSortField(String sortField) {
        this.sortField = sortField;
    }

    public void setSortAscending(boolean sortAscending) {
        this.sortAscending = sortAscending;
    }

    // Helpers -----------------------------------------------------------------------------------

    private static String getAttribute(ActionEvent event, String name) {
        return (String) event.getComponent().getAttributes().get(name);
    }

}

Session scope: you don't need to pre-instantiate the sortField and sortAscending with null and true as those won't be garbaged after one request. You also have to move the piece of Java code after the "// Sort results." comment at the end of sortDataList to the end of loadDataList. This is because the reloading of the dataList in the getDataList() method will override the sorting otherwise.

    public void loadDataList() {

        ...

        // Sort results.
        if (sortField != null) {
            Collections.sort(dataList, new DTOComparator(sortField, sortAscending));
        }
    }

Here is the DTOComparator.java which can be very useful to sort data transfer objects in a List. It implements the Comparator interface.

package myutil;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

/**
 * Sorts a collection of data transfer objects (DTO's) based on the name of the getter of the field
 * to sort on. For example: the DTO MyData has three fields ID, Name and Value, all with appropriate
 * getters and setters. If you want to sort on the field Name, which has a getter called "getName",
 * then invoke the sorting as follows:
 * <pre>
 * Collections.sort(dataList, new DTOComparator("getName", true));
 * </pre>
 * The following call is also supported. The "get" will be prefixed automatically if not present:
 * <pre>
 * Collections.sort(dataList, new DTOComparator("name", true));
 * </pre>
 * You can use "deep" getters when a DTO contains one or more nested DTO's. For example: MyData1
 * contains a MyData2 property which contains a String property "name". If you want to sort
 * myData1List on the "name" property of MyData2, then separate the getters by dots and invoke the
 * sorting as follows:
 * <pre>
 * Collections.sort(myData1List, new DTOComparator("myData2.name", true));
 * </pre>
 * The boolean 2nd parameter indicates the sort order. If true, then the collection will be sorted
 * ascending at natural sort order. If false, then the collection will be sorted descending at
 * natural sort order. The default value is true.
 * <p>
 * Very useful for lists of DTO's used in JSF datatables.
 *
 * @author BalusC
 * @link http://balusc.blogspot.com/2006/06/using-datatables.html
 */
public class DTOComparator implements Comparator<Object> {

    // Init --------------------------------------------------------------------------------------

    private List<String> getters;
    private boolean ascending;

    // Constructor -------------------------------------------------------------------------------

    /**
     * @param getter The name of the getter of the field to sort on.
     * @param ascending The sort order: true = ascending, false = descending.
     */
    public DTOComparator(String getter, boolean ascending) {
        this.getters = new ArrayList<String>();
        for (String name : getter.split("\\.")) {
            if (!name.startsWith("get")) {
                name = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
            }
            this.getters.add(name);
        }
        this.ascending = ascending;
    }

    /**
     * @param getter The name of the getter of the field to sort on.
     */
    public DTOComparator(String getter) {
        this(getter, true);
    }

    // Actions -----------------------------------------------------------------------------------

    /**
     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
     */
    @SuppressWarnings("unchecked")
    public int compare(Object o1, Object o2) {
        try {
            Iterator<String> iter = getters.iterator();
            while (o1 != null && o2 != null && iter.hasNext()) {
                String getter = iter.next();
                o1 = o1.getClass().getMethod(getter, new Class[0]).invoke(o1, new Object[0]);
                o2 = o2.getClass().getMethod(getter, new Class[0]).invoke(o2, new Object[0]);
            }
        } catch (Exception e) {
            // If this exception occurs, then it is usually a fault of the DTO developer.
            throw new RuntimeException("Cannot compare " + o1 + " with " + o2 + " on " + getters, e);
        }

        if (ascending) {
            return (o1 == null) ? -1 : ((o2 == null) ? 1 : ((Comparable<Object>) o1).compareTo(o2));
        } else {
            return (o2 == null) ? -1 : ((o1 == null) ? 1 : ((Comparable<Object>) o2).compareTo(o1));
        }
    }

}
Back to top

Paging datatable

Update: there's a newer article out for a more effective paging at DAO level. You may find it useful: Effective datatable paging and sorting.

If you have a plenty of rows, then you can use the following datatable attributes to page through the datatable: first and rows. The first attribute identifies the row number of the starting row which should be displayed. The rows attribute identifies the amount of rows to be shown at once, from the first row on. For pagination we need to set the first attribute dynamically in the backing bean. You can use commandlinks or commandbuttons to change this value.

So extend the datatable with the rows attribute and a set of commandbuttons in the footer. In this example we will page on every 3 rows.

<h:form>
    <h:dataTable binding="#{myBean.dataTable}" value="#{myBean.dataList}" var="dataItem" rows="3">
        <h:column>
            ...
        </h:column>

        ...

        <f:facet name="footer">
            <h:panelGroup>
                <h:commandButton value="first" action="#{myBean.pageFirst}"
                    disabled="#{myBean.dataTable.first == 0}" />
                <h:commandButton value="prev" action="#{myBean.pagePrevious}"
                    disabled="#{myBean.dataTable.first == 0}" />
                <h:commandButton value="next" action="#{myBean.pageNext}"
                    disabled="#{myBean.dataTable.first + myBean.dataTable.rows
                        >= myBean.dataTable.rowCount}" />
                <h:commandButton value="last" action="#{myBean.pageLast}"
                    disabled="#{myBean.dataTable.first + myBean.dataTable.rows
                        >= myBean.dataTable.rowCount}" />
            </h:panelGroup>
        </f:facet>
    </h:dataTable>
</h:form>

Please note the disabled attribute of the commandbuttons. The 'first' and the 'prev' buttons should be disabled at the first page. The 'next' and the 'last' buttons should be disabled when a next page cannot exist.

And add the following pagination methods to the backing bean:

package mypackage;

public class MyBean {

    // Actions -----------------------------------------------------------------------------------

    public void pageFirst() {
        dataTable.setFirst(0);
    }

    public void pagePrevious() {
        dataTable.setFirst(dataTable.getFirst() - dataTable.getRows());
    }

    public void pageNext() {
        dataTable.setFirst(dataTable.getFirst() + dataTable.getRows());
    }

    public void pageLast() {
        int count = dataTable.getRowCount();
        int rows = dataTable.getRows();
        dataTable.setFirst(count - ((count % rows != 0) ? count % rows : rows));
    }

}

With the following getters you can get the current page number and the total number of pages.

package mypackage;

public class MyBean {

    // Special getters ---------------------------------------------------------------------------

    public int getCurrentPage() {
        int rows = dataTable.getRows();
        int first = dataTable.getFirst();
        int count = dataTable.getRowCount();
        return (count / rows) - ((count - first) / rows) + 1;
    }

    public int getTotalPages() {
        int rows = dataTable.getRows();
        int count = dataTable.getRowCount();
        return (count / rows) + ((count % rows != 0) ? 1 : 0);
    }

}

Note that you need to handle ArithmeticException / by zero when the rows is 0 and you still want to display the table.

Back to top

Nesting datatables

You can use multidimensional lists in a nested datatable: just put the inner datatable in a h:column of the outer datatable and add the inner list to the DTO of the outer list.

The basic JSF code:

<h:dataTable value="#{myBean.dataList}" var="dataItem">
    <h:column>
        <h:outputText value="#{dataItem.name}" />
    </h:column>

    <h:column>
        <h:outputText value="#{dataItem.value}" />
    </h:column>

    <h:column>
        <h:dataTable value="#{dataItem.innerList}" var="innerItem">
            <h:column>
                <h:outputText value="#{innerItem.name}" />
            </h:column>

            <h:column>
                <h:outputText value="#{innerItem.value}" />
            </h:column>
        </h:dataTable>
    </h:column>
</h:dataTable>

The updated Java code of MyData.java which represents each dataItem:

package mypackage;

public class MyData {

    // Init --------------------------------------------------------------------------------------

    private List<MyData> innerList;

    // Getters -----------------------------------------------------------------------------------

    public List<MyData> getInnerList() {
        return innerList;
    }

    // Setters -----------------------------------------------------------------------------------

    public void setInnerList(List<MyData> innerList) {
        this.innerList = innerList;
    }

}
Back to top

Populate datatable

You can also programmatically populate the datatable in the backing bean. This may be useful if you don't know the expected state of the datatable in the JSF page, but only in the backing bean.

Here is the basic JSF code:


<h:panelGroup binding="#{myBean.dataTableGroup}" />

Fairly simple, isn't it? We're using a h:panelGroup as placeholder, because when you want to bind the datatable itself, then you have to specify the var attribute in the JSF page. Using a h:panelGroup without any style doesn't output anything to the HTML, so this won't harm that much. This approach is not needed anymore if you're using JSF 1.2_10 or newer, since this bug is been fixed.

And here is the relevant Java code of the backing bean, it might look like a lot of code, but it is actually rather simple:

package mypackage;

import java.util.List;

import javax.el.ValueExpression;
import javax.faces.component.html.HtmlColumn;
import javax.faces.component.html.HtmlDataTable;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.component.html.HtmlPanelGroup;

public class MyBean {

    // Init --------------------------------------------------------------------------------------

    private HtmlPanelGroup dataTableGroup; // Placeholder.

    // Actions -----------------------------------------------------------------------------------

    private void populateDataTable() {

        // Create <h:dataTable value="#{myBean.dataList}" var="dataItem">.
        HtmlDataTable dataTable = new HtmlDataTable();
        dataTable.setValueExpression("value",
            createValueExpression("#{myBean.dataList}", List.class));
        dataTable.setVar("dataItem");

        // Create <h:column> for 'ID' column.
        HtmlColumn idColumn = new HtmlColumn();
        dataTable.getChildren().add(idColumn);

        // Create <h:outputText value="ID"> for <f:facet name="header"> of 'ID' column.
        HtmlOutputText idHeader = new HtmlOutputText();
        idHeader.setValue("ID");
        idColumn.setHeader(idHeader);

        // Create <h:outputText value="#{dataItem.id}"> for the body of 'ID' column.
        HtmlOutputText idOutput = new HtmlOutputText();
        idOutput.setValueExpression("value",
            createValueExpression("#{dataItem.id}", Long.class));
        idColumn.getChildren().add(idOutput);

        // Create <h:column> for 'Name' column.
        HtmlColumn nameColumn = new HtmlColumn();
        dataTable.getChildren().add(nameColumn);

        // Create <h:outputText value="Name"> for <f:facet name="header"> of 'Name' column.
        HtmlOutputText nameHeader = new HtmlOutputText();
        nameHeader.setValue("Name");
        nameColumn.setHeader(nameHeader);

        // Create <h:outputText value="#{dataItem.name}"> for the body of 'Name' column.
        HtmlOutputText nameOutput = new HtmlOutputText();
        nameOutput.setValueExpression("value",
            createValueExpression("#{dataItem.name}", String.class));
        nameColumn.getChildren().add(nameOutput);

        // Create <h:column> for 'Value' column.
        HtmlColumn valueColumn = new HtmlColumn();
        dataTable.getChildren().add(valueColumn);

        // Create <h:outputText value="Value"> for <f:facet name="header"> of 'Value' column.
        HtmlOutputText valueHeader = new HtmlOutputText();
        valueHeader.setValue("Value");
        valueColumn.setHeader(valueHeader);

        // Create <h:outputText value="#{dataItem.value}"> for the body of 'Value' column.
        HtmlOutputText valueOutput = new HtmlOutputText();
        valueOutput.setValueExpression("value",
            createValueExpression("#{dataItem.value}", String.class));
        valueColumn.getChildren().add(valueOutput);

        // Finally add the datatable to <h:panelGroup binding="#{myBean.dataTableGroup}">.
        dataTableGroup = new HtmlPanelGroup();
        dataTableGroup.getChildren().add(dataTable);
    }

    // Getters -----------------------------------------------------------------------------------

    public HtmlPanelGroup getDataTableGroup() {
        // This will be called once in the first RESTORE VIEW phase.
        if (dataTableGroup == null) {
            populateDataTable(); // Populate datatable.
        }
        return dataTableGroup;
    }

    // Setters -----------------------------------------------------------------------------------

    public void setDataTableGroup(HtmlPanelGroup dataTableGroup) {
        this.dataTableGroup = dataTableGroup;
    }

    // Helpers -----------------------------------------------------------------------------------

    private ValueExpression createValueExpression(String valueExpression, Class<?> valueType) {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        return facesContext.getApplication().getExpressionFactory().createValueExpression(
            facesContext.getELContext(), valueExpression, valueType);
    }

}

This creates exactly the same datatable as shown in Show data contents in datatable.

Back to top

Populate action datatable

You can also programmatically populate a datatable with a binding and a backing bean action in every row in the backing bean. This may be useful if you don't know the expected state of the datatable in the JSF page, but only in the backing bean.

Here is the basic JSF code:


<h:panelGroup binding="#{myBean.actionDataTableGroup}" />

Fairly simple, isn't it? We're using a h:panelGroup as placeholder, because when you want to bind the datatable itself, then you have to specify the var attribute in the JSF page. Using a h:panelGroup without any style doesn't output anything to the HTML, so this won't harm that much. This approach is not needed anymore if you're using JSF 1.2_10 or newer, since this bug is been fixed.

And here is the relevant Java code of the backing bean, it might look like a lot of code, but it is actually rather simple:

package mypackage;

import java.util.List;

import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.component.html.HtmlColumn;
import javax.faces.component.html.HtmlCommandLink;
import javax.faces.component.html.HtmlDataTable;
import javax.faces.component.html.HtmlInputText;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.component.html.HtmlPanelGroup;

public class MyBean {

    // Init --------------------------------------------------------------------------------------

    private HtmlPanelGroup actionDataTableGroup; // Placeholder.
    private HtmlDataTable actionDataTable;

    // Actions -----------------------------------------------------------------------------------

    private void populateActionDataTable() {

        // Create <h:dataTable binding="#{myBean.dataTable}"
        // value="#{myBean.dataList}" var="dataItem">.
        HtmlDataTable actionDataTable = new HtmlDataTable();
        actionDataTable.setValueExpression("binding",
            createValueExpression("#{myBean.actionDataTable}", HtmlDataTable.class));
        actionDataTable.setValueExpression("value",
            createValueExpression("#{myBean.dataList}", List.class));
        actionDataTable.setVar("dataItem");

        // Create <h:column> for 'ID' column.
        HtmlColumn idColumn = new HtmlColumn();
        actionDataTable.getChildren().add(idColumn);

        // Create <h:outputText value="ID"> for <f:facet name="header"> of 'ID' column.
        HtmlOutputText idHeader = new HtmlOutputText();
        idHeader.setValue("ID");
        idColumn.setHeader(idHeader);

        // Create <h:commandLink value="#{dataItem.id}" action="#{myBean.editPopulatedDataItem}" />
        // for the body of 'ID' column.
        HtmlCommandLink idLink = new HtmlCommandLink();
        idLink.setId("edit"); // Custom ID is required in dynamic UIInput and UICommand.
        idLink.setValueExpression("value",
            createValueExpression("#{dataItem.id}", Long.class));
        idLink.setActionExpression(
            createActionExpression("#{myBean.editPopulatedDataItem}", String.class));
        idColumn.getChildren().add(idLink);

        // Create <h:column> for 'Name' column.
        HtmlColumn nameColumn = new HtmlColumn();
        actionDataTable.getChildren().add(nameColumn);

        // Create <h:outputText value="Name"> for <f:facet name="header"> of 'Name' column.
        HtmlOutputText nameHeader = new HtmlOutputText();
        nameHeader.setValue("Name");
        nameColumn.setHeader(nameHeader);

        // Create <h:outputText value="#{dataItem.name}"> for the body of 'Name' column.
        HtmlOutputText nameOutput = new HtmlOutputText();
        nameOutput.setValueExpression("value",
            createValueExpression("#{dataItem.name}", String.class));
        nameColumn.getChildren().add(nameOutput);

        // Create <h:column> for 'Value' column.
        HtmlColumn valueColumn = new HtmlColumn();
        actionDataTable.getChildren().add(valueColumn);

        // Create <h:outputText value="Value"> for <f:facet name="header"> of 'Value' column.
        HtmlOutputText valueHeader = new HtmlOutputText();
        valueHeader.setValue("Value");
        valueColumn.setHeader(valueHeader);

        // Create <h:outputText value="#{dataItem.value}"> for the body of 'Value' column.
        HtmlOutputText valueOutput = new HtmlOutputText();
        valueOutput.setValueExpression("value",
            createValueExpression("#{dataItem.value}", String.class));
        valueColumn.getChildren().add(valueOutput);

        // Finally add the datatable to <h:panelGroup binding="#{myBean.actionDataTableGroup}">.
        actionDataTableGroup = new HtmlPanelGroup();
        actionDataTableGroup.getChildren().add(actionDataTable);
    }

    public String editPopulatedDataItem() {

        // Get selected MyData item to be edited.
        dataItem = (MyData) actionDataTable.getRowData();

        return "edit"; // Navigation case.
    }

    // Getters -----------------------------------------------------------------------------------

    public HtmlPanelGroup getActionDataTableGroup() {
        // This will be called once in the first RESTORE VIEW phase.
        if (actionDataTableGroup == null) {
            populateActionDataTable(); // Populate action datatable.
        }
        return actionDataTableGroup;
    }

    public HtmlDataTable getActionDataTable() {
        return actionDataTable;
    }

    // Setters -----------------------------------------------------------------------------------

    public void setActionDataTableGroup(HtmlPanelGroup actionDataTableGroup) {
        this.actionDataTableGroup = actionDataTableGroup;
    }

    public void setActionDataTable(HtmlDataTable actionDataTable) {
        this.actionDataTable = actionDataTable;
    }

    // Helpers -----------------------------------------------------------------------------------

    private ValueExpression createValueExpression(String valueExpression, Class<?> valueType) {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        return facesContext.getApplication().getExpressionFactory().createValueExpression(
            facesContext.getELContext(), valueExpression, valueType);
    }

    private MethodExpression createActionExpression(String actionExpression, Class<?> returnType) {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        return facesContext.getApplication().getExpressionFactory().createMethodExpression(
            facesContext.getELContext(), actionExpression, returnType, new Class[0]);
    }

}

This creates exactly the same datatable as shown in Add backing bean action to every row.

Back to top

Populate editable datatable

You can also programmatically populate an editable datatable in the backing bean. This may be useful if you don't know the expected state of the datatable in the JSF page, but only in the backing bean.

Here is the basic JSF code:


<h:panelGroup binding="#{myBean.editableDataTableGroup}" />

Fairly simple, isn't it? We're using a h:panelGroup as placeholder, because when you want to bind the datatable itself, then you have to specify the var attribute in the JSF page. Using a h:panelGroup without any style doesn't output anything to the HTML, so this won't harm that much. This approach is not needed anymore if you're using JSF 1.2_10 or newer, since this bug is been fixed.

And here is the relevant Java code of the backing bean, it might look like a lot of code, but it is actually rather simple:

package mypackage;

import java.util.List;

import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.component.html.HtmlColumn;
import javax.faces.component.html.HtmlCommandButton;
import javax.faces.component.html.HtmlDataTable;
import javax.faces.component.html.HtmlInputText;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.component.html.HtmlPanelGroup;

public class MyBean {

    // Init --------------------------------------------------------------------------------------

    private HtmlPanelGroup editableDataTableGroup; // Placeholder.

    // Actions -----------------------------------------------------------------------------------

    private void populateEditableDataTable() {

        // Create <h:dataTable value="#{myBean.dataList}" var="dataItem">.
        HtmlDataTable editableDataTable = new HtmlDataTable();
        editableDataTable.setValueExpression("value",
            createValueExpression("#{myBean.dataList}", List.class));
        editableDataTable.setVar("dataItem");

        // Create <h:column> for 'ID' column.
        HtmlColumn idColumn = new HtmlColumn();
        editableDataTable.getChildren().add(idColumn);

        // Create <h:outputText value="ID"> for <f:facet name="header"> of 'ID' column.
        HtmlOutputText idHeader = new HtmlOutputText();
        idHeader.setValue("ID");
        idColumn.setHeader(idHeader);

        // Create <h:outputText value="#{dataItem.id}"> for the body of 'ID' column.
        HtmlOutputText idOutput = new HtmlOutputText();
        idOutput.setValueExpression("value",
            createValueExpression("#{dataItem.id}", Long.class));
        idColumn.getChildren().add(idOutput);

        // Create <h:column> for 'Name' column.
        HtmlColumn nameColumn = new HtmlColumn();
        editableDataTable.getChildren().add(nameColumn);

        // Create <h:outputText value="Name"> for <f:facet name="header"> of 'Name' column.
        HtmlOutputText nameHeader = new HtmlOutputText();
        nameHeader.setValue("Name");
        nameColumn.setHeader(nameHeader);

        // Create <h:inputText id="name" value="#{dataItem.name}"> for the body of 'Name' column.
        HtmlInputText nameInput = new HtmlInputText();
        nameInput.setId("name"); // Custom ID is required in dynamic UIInput and UICommand.
        nameInput.setValueExpression("value",
            createValueExpression("#{dataItem.name}", String.class));
        nameColumn.getChildren().add(nameInput);

        // Create <h:column> for 'Value' column.
        HtmlColumn valueColumn = new HtmlColumn();
        editableDataTable.getChildren().add(valueColumn);

        // Create <h:outputText value="Value"> for <f:facet name="header"> of 'Value' column.
        HtmlOutputText valueHeader = new HtmlOutputText();
        valueHeader.setValue("Value");
        valueColumn.setHeader(valueHeader);

        // Create <h:inputText id="value" value="#{dataItem.value}"> for the body of 'Value' column.
        HtmlInputText valueInput = new HtmlInputText();
        valueInput.setId("value"); // Custom ID is required in dynamic UIInput and UICommand.
        valueInput.setValueExpression("value",
            createValueExpression("#{dataItem.value}", String.class));
        valueColumn.getChildren().add(valueInput);

        // Add the datatable to <h:panelGroup binding="#{myBean.editableDataTableGroup}">.
        editableDataTableGroup = new HtmlPanelGroup();
        editableDataTableGroup.getChildren().add(editableDataTable);

        // Create <h:outputText value="<br/>" escape="false"> and add to <h:panelGroup
        // binding="#{myBean.editableDataTableGroup}">.
        HtmlOutputText lineBreak = new HtmlOutputText();
        lineBreak.setValue("<br/>");
        lineBreak.setEscape(false); // Don't escape HTML.
        editableDataTableGroup.getChildren().add(lineBreak);

        // Create <h:commandButton id="save" value="Save" action="#{myBean.saveDataList}">
        // and add to <h:panelGroup binding="#{myBean.editableDataTableGroup}">.
        HtmlCommandButton saveButton = new HtmlCommandButton();
        saveButton.setId("save"); // Custom ID is required in dynamic UIInput and UICommand.
        saveButton.setValue("Save");
        saveButton.setActionExpression(
            createActionExpression("#{myBean.saveDataList}", String.class));
        editableDataTableGroup.getChildren().add(saveButton);
    }

    // Getters -----------------------------------------------------------------------------------

    public HtmlPanelGroup getEditableDataTableGroup() {
        // This will be called once in the first RESTORE VIEW phase.
        if (editableDataTableGroup == null) {
            populateEditableDataTable(); // Populate editable datatable.
        }
        return editableDataTableGroup;
    }

    // Setters -----------------------------------------------------------------------------------

    public void setEditableDataTableGroup(HtmlPanelGroup editableDataTableGroup) {
        this.editableDataTableGroup = editableDataTableGroup;
    }

    // Helpers -----------------------------------------------------------------------------------

    private ValueExpression createValueExpression(String valueExpression, Class<?> valueType) {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        return facesContext.getApplication().getExpressionFactory().createValueExpression(
            facesContext.getELContext(), valueExpression, valueType);
    }

    private MethodExpression createActionExpression(String actionExpression, Class<?> returnType) {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        return facesContext.getApplication().getExpressionFactory().createMethodExpression(
            facesContext.getELContext(), actionExpression, returnType, new Class[0]);
    }

}

This creates exactly the same datatable as shown in Editable datatable.

Back to top

Populate dynamic datatable

You also can dynamically populate a datatable in the backing bean. This may be very useful if you are using dynamic generated two-dimensional lists (for example, uploaded CSV files, local text files, DB fields, etc) and you don't know the amount of columns before.

The basic JSF code:


<h:panelGroup binding="#{myBean.dynamicDataTableGroup}" />

The relevant Java code of the backing bean:

package mypackage;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.el.ValueExpression;
import javax.faces.component.HtmlColumn;
import javax.faces.component.HtmlOutputText;
import javax.faces.component.html.HtmlDataTable;
import javax.faces.context.FacesContext;

public class MyBean {

    // Init --------------------------------------------------------------------------------------

    private static List<List<String>> dynamicList; // Simulate fake DB.
    private static String[] dynamicHeaders; // Optional.
    private HtmlPanelGroup dynamicDataTableGroup; // Placeholder.

    // Actions -----------------------------------------------------------------------------------

    private void loadDynamicList() {

        // Set headers (optional).
        dynamicHeaders = new String[] {"ID", "Name", "Value"};

        // Set rows. This is a stub example, just do your dynamic thing.
        dynamicList = new ArrayList<List<String>>();
        dynamicList.add(Arrays.asList(new String[] { "ID1", "Name1", "Value1" }));
        dynamicList.add(Arrays.asList(new String[] { "ID2", "Name2", "Value2" }));
        dynamicList.add(Arrays.asList(new String[] { "ID3", "Name3", "Value3" }));
        dynamicList.add(Arrays.asList(new String[] { "ID4", "Name4", "Value4" }));
        dynamicList.add(Arrays.asList(new String[] { "ID5", "Name5", "Value5" }));
    }

    private void populateDynamicDataTable() {

        // Create <h:dataTable value="#{myBean.dynamicList}" var="dynamicItem">.
        HtmlDataTable dynamicDataTable = new HtmlDataTable();
        dynamicDataTable.setValueExpression("value",
            createValueExpression("#{myBean.dynamicList}", List.class));
        dynamicDataTable.setVar("dynamicItem");

        // Iterate over columns.
        for (int i = 0; i < dynamicList.get(0).size(); i++) {

            // Create <h:column>.
            HtmlColumn column = new HtmlColumn();
            dynamicDataTable.getChildren().add(column);

            // Create <h:outputText value="dynamicHeaders[i]"> for <f:facet name="header"> of column.
            HtmlOutputText header = new HtmlOutputText();
            header.setValue(dynamicHeaders[i]);
            column.setHeader(header);

            // Create <h:outputText value="#{dynamicItem[" + i + "]}"> for the body of column.
            HtmlOutputText output = new HtmlOutputText();
            output.setValueExpression("value",
                createValueExpression("#{dynamicItem[" + i + "]}", String.class));
            column.getChildren().add(output);
        }

        // Add the datatable to <h:panelGroup binding="#{myBean.dynamicDataTableGroup}">.
        dynamicDataTableGroup = new HtmlPanelGroup();
        dynamicDataTableGroup.getChildren().add(dynamicDataTable);
    }

    // Getters -----------------------------------------------------------------------------------

    public HtmlPanelGroup getDynamicDataTableGroup() {
        // This will be called once in the first RESTORE VIEW phase.
        if (dynamicDataTableGroup == null) {
            loadDynamicList(); // Preload dynamic list.
            populateDynamicDataTable(); // Populate editable datatable.
        }

        return dynamicDataTableGroup;
    }

    public List<List<String>> getDynamicList() {
        return dynamicList;
    }

    // Setters -----------------------------------------------------------------------------------

    public void setDynamicDataTableGroup(HtmlPanelGroup dynamicDataTableGroup) {
        this.dynamicDataTableGroup = dynamicDataTableGroup;
    }

}
Back to top

Add row numbers

This is fairly simple to implement using getRowIndex() method of the HtmlDataTable class, which is bound by h:dataTable binding.

<h:dataTable binding="#{myBean.dataTable}">
    <h:column>
        <f:facet name="header">
            <h:outputText value="Row #" />
        </f:facet>
        <h:outputText value="#{myBean.dataTable.rowIndex + 1}" />
    </h:column>

    ...
</h:dataTable>

We are using + 1 in the value binding expression, because the row index usually starts with 0.

Back to top

Alternating rows

You can alternate the rows using the dataTable attribute rowClasses and some piece of CSS. Put in there at least two style class names and the rows will apply those styles concurrently and repeating. Here is a basic example:

<h:dataTable rowClasses="row1, row2">
    ...
</h:dataTable

Here is the CSS defining the row1 and row2 styles. This is just a basic example, you can define your own colors for example.

.row1 {
    background-color: #ddd;
}
.row2 {
    background-color: #bbb;
}
Back to top

Highlight rows on click

You can use Javascript and DOM to alter the rows of the dataTable by adding an onclick function to it which triggers a Javascript function. Here is a basic Javascript example:

function addOnclickToDatatableRows() {
    var trs = document.getElementById('dataTable').getElementsByTagName('tbody')[0]
        .getElementsByTagName('tr');
    for (var i = 0; i < trs.length; i++) {
        trs[i].onclick = new Function("highlightRow(this)");
    }
}

function highlightRow(tr) {
    tr.bgColor = (tr.bgColor != '#ff0000') ? '#ff0000' : '#ffffff';
}

Call addOnclickToDatatableRows() during onload of the window. Take note that the element ID in the Javascript have to be exactly the same as the rendered element ID of the table.

<head>
    <script>
        window.onload = addOnclickToDatatableRows;
    </script>
</head>

<body>
    <h:dataTable id="dataTable">
        ...
    </h:dataTable>
</body>
Back to top

Highlight and select row on click

You can also highlight single row and select the row for submit using an onclick function on the table row. Basically just pass its row index to a plain vanilla HTML hidden input. Here is a basic Javascript example:

function addOnclickToDatatableRows() {
    var trs = document.getElementById('form:dataTable').getElementsByTagName('tbody')[0]
        .getElementsByTagName('tr');
    for (var i = 0; i < trs.length; i++) {
        trs[i].onclick = new Function("highlightAndSelectRow(this)");
    }
}

function highlightAndSelectRow(tr) {
    var trs = document.getElementById('form:dataTable').getElementsByTagName('tbody')[0]
        .getElementsByTagName('tr');
    for (var i = 0; i < trs.length; i++) {
        if (trs[i] == tr) {
            trs[i].bgColor = '#ff0000';
            document.form.rowIndex.value = trs[i].rowIndex;
        } else {
            trs[i].bgColor = '#ffffff';
        }
    }
}

Call addOnclickToDatatableRows() during onload of the window. Take note that the element ID in the Javascript have to be exactly the same as the generated element ID of the form and the table.

<head>
    <script>
        window.onload = addOnclickToDatatableRows;
    </script>
</head>

<body>
    <h:form id="form">
        <h:dataTable id="dataTable">
            ...
        </h:dataTable>
        <input type="hidden" name="rowIndex" />
        <h:commandButton value="Edit selected row" action="#{myBean.editDataItem}" />
    </h:form>
</body>

Here is the relevant part of the backing bean code. This achieves exactly the same actions as shown in Add backing bean action to every row and Get selected datatable row:

package mypackage;

import java.util.List;

import javax.faces.context.FacesContext;

public class MyBean {

    // Init --------------------------------------------------------------------------------------

    private List<MyData> dataList;
    private MyData dataItem = new MyData();

    // Actions -----------------------------------------------------------------------------------

    public String editDataItem() {

        // Obtain the row index from the hidden input element.
        String rowIndex = FacesContext.getCurrentInstance().getExternalContext()
            .getRequestParameterMap().get("rowIndex");
        if (rowIndex != null && rowIndex.trim().length() != 0) {
            dataItem = dataList.get(Integer.parseInt(rowIndex));
        } else {
            // Handle unexpected state, e.g. show message "Please select row" or so.
        }

        return "edit"; // Navigation case.
    }

}
Back to top

Highlight rows on hover

You can use Javascript and DOM to alter the rows of the dataTable by adding an onmouseover and onmouseout function to it which triggers a Javascript function. Here is a basic Javascript example:

function addHoverToDatatableRows() {
    var trs = document.getElementById('dataTable').getElementsByTagName('tbody')[0]
        .getElementsByTagName('tr');
    for (var i = 0; i < trs.length; i++) {
        trs[i].onmouseover = new Function("this.bgColor='#ff0000'");
        trs[i].onmouseout = new Function("this.bgColor='#ffffff'");
    }
}

Call addHoverToDatatableRows() during onload of the window. Take note that the element ID in the Javascript have to be exactly the same as the generated element ID of the table.

<head>
    <script>
        window.onload = addHoverToDatatableRows;
    </script>
</head>

<body>
    <h:dataTable id="dataTable">
        ...
    </h:dataTable>
</body>
Back to top

Customized tables

If you're using at least JSF 1.2 and JSTL 1.2 in a JSP 2.1 container (available in Java EE 5 server, e.g. Tomcat 6.0 or Glassfish), then you can access JSF managed beans in the JSTL c:forEach tag, thanks to the unified EL. This will give you more freedom to create exotic tables based on a collection of DTO's from a managed bean, e.g. introducing colspans and so on.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>

...

<h:form>
    <table>
        <thead>
            <tr>
                <th colspan="3">MASTER TABLE</th>
            </tr>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Value</th>
            </tr>
        </thead>
        <tbody>
            <c:forEach items="#{myBean.dataList}" var="dataItem" varStatus="status">
                <f:verbatim rendered="#{dataItem.id % 2 != 0}">
                    <tr style="background-color: red;">
                        <td colspan="3">This is just an example of an alternately inserted row.</td>
                    </tr>
                </f:verbatim>
                <tr style="background-color: ${status.index % 2 == 0 ? 'lightgray' : 'gray'};">
                    <td>
                        <h:commandLink value="#{dataItem.id}" action="#{myBean.editDataItem}">
                            <f:setPropertyActionListener
                               target="#{myBean.dataItem}" value="#{dataItem}" />
                        </h:commandLink>
                    </td>
                    <td><h:outputText value="#{dataItem.name}" /></td>
                    <td><h:outputText value="#{dataItem.value}" /></td>
                </tr>
            </c:forEach>
        </tbody>
    </table>
</h:form>

This creates a table with exactly the same behaviour as the table as shown in Add backing bean action to every row with the difference that you can introduce colspans to your taste. The example also shows how to use f:verbatim whether to render plain vanilla HTML or not. The example also shows how to alternate the row colors using the c:forEach varStatus. The f:setPropertyActionListener (which was introduced in JSF 1.2 and only works inside UICommand components) will set the current dataItem object in the appropriate property of the backing bean. It is the same dataItem object as you should obtain by HtmlDataTable#getRowData() when using a regular h:dataTable.

Back to top

Download WAR

Here is a WAR containing all of the above examples in Request as well as Session scope. This WAR also contains "The ultimate CRUD example" which is a show-off of the capacities of datatables (and JSF itself).

UsingDatatables.war (1.49 MB, updated at 2008-12-08), based on JSF 1.2_10 in a Java EE 5.0 environment.

This WAR is developed and tested in Eclipse 3.4 using Tomcat Application Server 6.0.18.

  

Back to top

Copyright - There is no copyright on the code. You can steal, change and distribute it freely. Just mentioning this site should be fair.

(C) June 2006, BalusC

297 comments:

1 – 200 of 297   Newer›   Newest»
Vishwanath said...

i gone throgh your post "Using datatables" for creating pagination in JSF. but i didn't understood the "mydao" which you have importing it in java class.
is it folder or api or any custom class. Please let me know what is mydao. becouse of this am not getting any thing. please reply.


my email id
vishwanath.bannur@shivasoftware.com


Regards,
Vishwanath

BalusC said...

The literal package name "mydao" ought to be straightforward enough. Also the comments like "Do your "SELECT * FROM mydata" thing." should be clear enough. This article handles the using of h:dataTable in detail, not how you handle the actual data.

Surendra Chowdary said...

Hi BalusC, i am very thankful to u for ur post "Using datatables". it is very useful for me in my project. I need another help from u regarding TabbedPanes. The problem is my jsf page has to show the tabbedpane with tabs and the number of tabs should depend on the property in the bean which is obtained from the list. And each tab should display its own information as in the bean. Moreover we should able to make changes to the data in tab and save it back to the backing bean.


please give suggestions on this how to do. reply me to surendrachowdary.e@gmail.com. i will be waiting for ur reply

Thanks in advance
Regards,
Surendra

Joe said...

Thank you for your great articel about datatable, i just have one question. Is it possible to have dynamic columns and rows with inputtext fields?

BalusC said...

Surendra: This is out of the scope of this article. I suggest you to read the documentation of the tabbedpanel component you're going to use.

Joe: Surely it's possible. I'll extend the article with that information as soon as I have time.

viking said...

Hi,
Your article is verry usefull and I thank you for that.
But I have question too:
What about exporting(xls,pdf) data of the table?

mitch said...

Downloading the .ear files is not working today. The connection times out.

Tiago said...

Are you sure it's possible to have a commandlink working in a datatable with a backing bean with the request scope?

I tried to make this work with SUN's JSF 1.2_04 p02 without success.

Thanks in advance.

BalusC said...

@Joe: the article is updated.

@viking: there are 3rd party API's for that. Google on that. Exporting to CSV isn't that hard, you can write it yourself, also see the "Parse CSV upload" article in this blog.

@mitch: I've removed the links, the EAR's are outdated in perspective to the article. A newer EAR will come up sooner or later. I'll also look for a better hoster meanwhile.

@Tiago: then you're probably doing something wrong with the data loading logic. Take over the code given in this article and don't change it. You'll see that it works.

Seba said...

Wow,
This is a great tutorial for jsf data table. I have long put off using jsf and this has convinced me to give it another chance. Thank you

Joe said...

Thanks for the great post!
But I am having a headache about how to display set of records page by page, one record each page with irregualr layout...
Can you help me or just knock my head, thanks!
Joe

happyFace said...

Balusc, I have tried to deploy the datatable ear file in glassfish and I am not having any luck. I get an error: The requested resource () is not available. I can't even hit the index.jsf. Is importing the Ear file supposed to be the only thing I should do? Thanks! melvingant (at) gmail dot com

BalusC said...

Likely you're using the wrong context root. Check the web.xml what it is. In the case of the current EAR it is "UsingDatatables12", so it should be accessible on http://localhost:8080/UsingDatatables12

happyFace said...

Thanks,

It now works!

Joe said...

Hi BalusC, I can accomplish the datatable paging with session scope, but unable to with request scope. Do you have any ideas of what I'm doing wrong? thx for your help!

BalusC said...

Maybe you're doing something wrong. At least the article is focused on the request scope and the EAR contains an example of paging in request scope.

kunjal said...

Excellent post... Helped me a lot with JSF. One question I had was regarding have paging functionality in a dynamically populated datatable... my issue is that the pageNext, pagePrev methods are not getting called.. I tried to create the paging buttons in the bean itself..
HtmlPanelGroup buttonGroup = new HtmlPanelGroup();
List c = buttonGroup.getChildren();
HtmlCommandButton first = new HtmlCommandButton();

first.setAction(app.createMethodBinding("#{DynamicViewBean.pageFirst}",null));
first.setActionListener(app.createMethodBinding("#{DynamicViewBean.pageFirst}",null));
first.setValue("first");

HtmlCommandButton prev = new HtmlCommandButton();
prev.setAction(app.createMethodBinding("#{DynamicViewBean.pagePrevious}",null));
prev.setValue("prev");

HtmlCommandButton next = new HtmlCommandButton();
next.setAction(app.createMethodBinding("#{DynamicViewBean.pageNext}",null));
//next.setActionListener(app.createMethodBinding("#{DynamicViewBean.pageNext}",new Class[]{ActionEvent.class}));
next.setValue("next");
but somehow the Action's are not called Any ideas ?

BalusC said...

At least also assign an ID to the buttons.

kunjal said...

resolved the issue.. the problem was with other form elements on the page and some validation error :) due to which the button actions were not called.. thanks for the helpful article..

jeremyrdavis said...

Nice post - very useful!

huiyi said...

very informative session.
was wondering if you could point me in the right direction.
i was trying to combine the multiple selection with checkboxes and the paging sections together, but if i select some options on the first page and then change to another and select some more, then go back to the previous pages, the previous selection does not show. am i doing something wrongly?


mei
would appreciate any help. thanks

BalusC said...

You'll have to store them in session. Get the EAR and checkout the CRUD example. It demonstrates exactly what you want.

kss said...

Hi I was looking at your code to sort the DataList within DataTable.. Now my issue is that the DataTable is generated dynamically in the backing bean.. How do I pass the attribute parameter to the HtmlCommandLink with the name/value....In my case the DataTable column value is bound this way...

HtmlOutputText output = new HtmlOutputText();
output.setValueBinding("value",app.createValueBinding("#{dynamicItem[" + i + "]}"));


How do I create a HtmlCommandLink with the parameter value as the get method of the Bean..

Any ideas ?

BalusC said...

Use UIComponent#getAttributes(). It returns the attribute map and you can use it the same way like f:attribute.

Mimi said...

Hello Balusc,
I'am using t:selectOneMenu in t:datatable. In each row it's a selectOneMenu, one (group1) with 4 items (Key: "0", "1", "2", "3") and one (group2) with 3 items (Key: "0", "1", "3"). Here the problem:
Only if I select in group1 the value with Key="2" and press the save-button, I become a "validation error: Value not valid". The values are String.
The Html-Code looks also right.
I'am using MyFaces1.1.3 and Tomahawk1.1.2.
How can I solve this problem? Have you an idea?

Thanks in advance
Regards,
Mimi

BalusC said...

This error can occur if in the next request the selected value isn't one of the values of the list. So all what you need to do is to make sure that in the next request the SelectItem list/array contains exactly the same values as during the form submit and that the selected value matches with one of them.

Tonda said...

Great job, thank you.
I have problem. I have tried to update an item in a nesting datatable, but it doesn't work. When I check the values of updated item in a backing bean, there is no change (I use list of ListDataModels wrapped in root ListDataModel). I have also tried to use a binding parameter in inner h:dataTable binding to extra DTO in the backing bean but this doesn't work as well (properties are not changed). When I check parameters from request, I can see a change of item's properties, but it hasn't any influence to ListDataModel in a backing bean.
Thank you, Tony.

BalusC said...

Maybe your data loading logic is wrong. Hard to say without actual code. Read the "Editable datatable" and "Nested datatable" paragraphs of this article and play around with the given code examples.

patel said...

Hey Balusc,
Its an excellent article on insigh of datatable, however it would be helpful if you could guide on tips of look and feel of table like scrolling body and fixed head

Thanks,
Prakash

Hobi said...

Balus, I could not download UsingDatatables12EAR.ear
Would you email it to me, hobione@gmail.com
Thank you

Hobi said...

I got it, it must be a some kind of server error at that time.
BalusC, you should write a book about JSF. You are good.
Thanks
Hobi
Oklahoma, USA

Louis M said...

Excellent reference for JSF datatables. Please never remove this post! It has saved me countless hours of research, and I will probably want to reference it again numerous times in the future.

naresh said...

It's a good info. But I want a scroll bar for data table, but the header should not move when we scroll down.

Paris said...

hi, i think i'm having trouble in distinguishing binding vs value @_@

Paris said...

I already figured it out. Thanks :) you have a really nice tutorial here

Sathya said...

Hi Balu sir,

This article is very nice and I am very thankful to u for this "using datatables"..It is very useful to me..
Once again thank u very much...

Regards,
Sathya,
Hyderabad,
India

Jim Driscoll said...

Brilliant article, thank you. Better than most books on the topic - you should consider doing one.

rajeev said...

i have gone through "Select row by radio button"

can u please give the code in jdk1.4 and what is dataItem,dataTable and selectedDataList i could not understand.

please give a soln in JDK 1.4..
Jscript and JSP will be same..

Dmitriy said...

Thanks for grate post! You articles - excellent help for n00b in JSF :)

NADIe said...

ThanKs!!

a great article and resources.

Irina Kostitsyna said...

BalusC,
Thanks a lot for such manual. It's the best I could find on datatables. However, I think you have couple mistypes in "Customized tables". At least, I couldn't make it work until I changed '#' on '$' in <c:forEach...> and '<h:outputText...>' on '<c:out ...>'. Or maybe I use different versions of libs or something else?

T22 said...

Thank you for the article. This is the best and most complete of any I've seen. Well done.

Maik said...

Great article. Thank you for that.

When invoking an action using a commandLink inside the dataTable, the action "loadMyDataList" is invoked BEFORE the "editMyData" action (see MyRequestBean).

Is there also a way to invoke the reload the data AFTER the commandLink action is invoked?

A use case for this is deleting a database row, so you need to refresh data after it has been deleted.

BalusC said...

No. The datatable component in request scope needs to know what data exactly was all listed in the table, so that it under each knows which row object it should return by getRowData().

Solution for your problem: use a session bean for data, load data once in constructor and reload data whenever to your taste after update and delete actions.

Another solution which keeps the bean request scoped: manually remove the deleted item from the dataList using List#remove() instead of reloading the whole dataList.

BalusC said...

Additional note: also see the CRUD example in the EAR for the session bean logic.

Dennis said...

Hello BalusC, you have a very nice article but I'm sorry for this newbie question... Can you post some code or just a link to another article on how to load data in the datatable using List in the backing bean without the use of generics? I've been googling for days but I can't find what I'm looking for.

BalusC said...

It's nothing special. As stated in the comments, just do your DAO thing. The given DAO examples are purely illustrative.

Olivier Liechti said...

Great article! I am trying to implement a CRUD example with request-scope bean. Using your examples, I have almost got it to work.

I still have one problem: when I am on the "Edit details" page and click on a "cancel" or "save" button, I go back to the "List" page. I always go back to the first page. How can I go back to the page where I was before going to the details page?

Other question: do you ususally work with a request-scope bean or session-scope bean in your apps?

Sirisha said...

hi,
my requirement is I have to display some data in dataTable and I need to put fixed height for the columns.if the data in that column exceeds then the data should hide and onmouseover one tooltip should come showing the exceeded data.

but the jsf data table is not hiding the data at all.I have given width and height for that column but it is showing the data completely..

please tell me how can I do this one.

Mirror Spock said...

Hi. I have implemented your code for datatable pagination and I am wondering how I can reset the "first" counter back to zero if I read in a new set of data. As far as I can tell, I can't directly set it as the backing bean doesn't come into scope until the page is being rendered.

Thanks,
* Robinson

srikanth said...

I have used pagination part of your blog. It is simple and effective. Thank you very much for your great blog.

Keep doing it. Because world needs it.

Romal said...

Hello,

What is the best way to trasnlate a value from the database into a text in the datatable?

Thanks,
Romal

Ashok said...

Hi balusC, very good article. I have an issue with dynamically generated datatable.

I wish to use a .GIF image as a command button.

I see that there is the option to set value as follows:

_downButton.setValue("");

I wish to use something like
_downButton.setImage(path) (path is the iimage path) but my API does not have such an option. There is however something like setHImage, setDImage, setPImage which does not however show any results. Please advice.

Thanks,
Ashok.

BalusC said...

@Romal: start learning the JDBC API.

@Ashok: use setAttribute("image", imageUrl).

Ashok said...

UIColumn _editColumn = new UIColumn();
HtmlCommandExButton _editCommand = new HtmlCommandExButton();

String _editActionListener = "#{bkgHandler.editBkgLP}";
String _editAction = "#{bkgHandler.editBkgLPOutcome}";
_editCommand.setId("cmdEdit" + num + type);
Class[] params = {ActionEvent.class };
MethodBinding _editActionListenerBinding = application.createMethodBinding(_editActionListener,params);
MethodBinding _editActionBinding = application.createMethodBinding(_editAction,null);
_editCommand.setActionListener(_editActionListenerBinding); _editCommand.setAction(_editActionBinding);
_editCommand.getAttributes().put("paramNum",Integer.toString(num));
_editCommand.getAttributes().put("paramType",Integer.toString(type));
_editCommand.setStyleClass("commandButton");

if (currentFlt.isA()) {
_editCommand.setOnclick("setPress('popup','A')");
}else {
_editCommand.setOnclick("setPress('popup','B')");
}
_editCommand.setValue("Edit");
_editColumn.getAttributes().put("width","20px");
_editColumn.getChildren().add(_editCommand);
_dataTable.getChildren().add(_editColumn);

The above code works perfectly for a commandButton. However, I have now to use a commandLink instead, So i tried to change commandButton to commandLink as follows:

HtmlCommandLink _editCommand = new HtmlCommandLink();

This does not work to deliver a commandLink. What am I missing here. What can I do to get a commandLink? Please help and advice.

Thanks,
Ashok.

BalusC said...

This renders an <a></a> element without any body value. You need to add some body value as child of HtmlCommandLink, e.g. a HtmlOutputText with a value "click here".

Ashok said...

Thank you so much. The link appeared. I am now having trouble bring up the pop-up page now. Also, with the command Button(edit), I was able to get row contents from the dataModel for edit onto a pop-up page.

With the commandLink, the .jsp page which is supposed to pop-up is opening as a seperate page.

Ingo said...

I go through the document. It is very useful. But I have an issue. How to add a datatable as a row in another datatable?

Stef said...

BalusC et al,

Has anyone tried binding to a nested datatable?
For example lets say we have an Pallete managed bean which contains a List of Box and in the Box object there is a List of Item and a binding to the item datatable.

In the page we have a datatable listing the Boxes, with var="currentBox", and in a column of this table we have a nested datatable which lists the items (value="#{currentBox.items}").

This works ok, but when trying to do a binding to the inner datatable like binding="#{currentBox.itemTable}" there is an exception as follows:

/test.xhtml @55,90 binding="#{currentBox.itemTable}": Target Unreachable, identifier 'currentBox' resolved to null

I am using:
JSF 1.1 (tried on both MyFaces and SUN RI)
Facelets 1.1.14

Your help will be appreciated!

Life is Beautiful said...

Hi Balu,

The article on daatable helped me a lot. I have an additional requirement to have the feature where the user may add rows to data table
Also, I do not want to hit the server, while adding rows. I am able to add rows using javascript but not able to get the values back. Could you suggest some ways to do the same?

BalusC said...

@Stef: bind it to the parent bean. There is only one component in the view, so you should not bind it to the row object.

@Life: not possible without server side interaction. JSF must be notified somehow that it must prepare the list with new rows. If you want to do this asynchronously, consider Ajax4jsf.

Ashok said...

BalusC,

I am still struggling with the concept of poping up a window by clicking a commandLink. The js function works perfectly with a Command Button but not with a commandLink. I changed the setOnClick to setTarget without any luck though. Please advice.

Kindly read my earlier post on the same for ref.

Thanks,
AShok.

BalusC said...

The onclick must return false to prevent the default action being fired.

dana said...

Hello,

This is a great post and I would love to learn from the attached code. I have a technical problem - I am using NetBeans and not sure how I can watch the attached examples and if it is possible, at all?

How can I extract the code to run it in NetBeans?

Thank you,
Dana

mahesh said...

Hi Bauke,

I have a requirement of highlighting the textBox (inputText) when the validation fails. Can you please give me an idea of how can I achieve this.

Thanks and Regards,
Niki

BalusC said...

Use JavaScript. You can find here an example: http://balusc.blogspot.com/2007/12/set-focus-in-jsf.html.

jsage said...

I have found your site to be the most helpful in discovering on to create dynamic objects in JSF. I used your examples to create a list of dynamic check boxes (ajax driven) and to create a dynamic data table.

What I was hoping you could help me with is it possible to display a datatable split between 2 rows? I have a limited amount of space on the page and would like to 'wrap' one column of a datatable below the other information for that row?

BalusC said...

@jsage: put <br> in cells or check the 'customized table' section.

@everyone who is interested: I've updated the code behind the downloadable EAR file and replaced it by a WAR file. The DAO logic is more simplified and the code updated according to the current version of the article (and vice versa). The CRUD example is also slightly enhanced (better validation and so on).

jsage said...

balusc, Thank you for the quick response. Using the 'Customized tables' was a snap. Is there anyway of doing this with just a normal jsf datatable (non dynamically generated)? Thanks again

Michal said...

Great tutorial!

I tried to make editable table as you suggested. Maybe I'm doing something wrong though.
On each row I have inputtext & commandbutton. It seems, that values in backing bean dont update except for the last row (nor valuechangelistener is called).

Cant post my code as I write from mobile phone.

Can you advise me something on this issue?

Thanks in advance!

BalusC said...

You have apparently bound the inputText to a single bean property instead of to a row object property (in terms of this tutorial, you apparently bound it to #{myBean.value} instead of #{dataItem.value}).

Michal said...

Thank you for immediate response!

This is not the case though. I bind inputboxes same way as other noneditable columns and values print out correctly. Just setters are not called.

With no success I tried also component binding.

I dont know if that matters as I am complete beginner, but I have Netbeans 6.1 with JSF 1.1 plugin.

Michal said...

Sorry for posting this spam but I am desperate and would appreciate any help.

I posted my code here:

http://forum.builder.cz/read.php?15,2670515

OUTPUT ON INITIAL PAGE LOAD:
id get 854
vstup get 854
id get 1102
vstup get 1102
id get 1103
vstup get 1103
id get 1152
vstup get 1152



OUTPUT ON NONLAST ROW DATA ENTER AND ITS BUTTON CLICK
854 Default text
id get 854
vstup get 854
id get 1102
vstup get 1102
id get 1103
vstup get 1103
id get 1152
vstup get 1152



OUTPUT ON LAST ROW DATA ENTER AND ITS(LAST) BUTTON CLICK
vstup get 1152
AHOJ
vstup set 1152
1152 New entered text
id get 854
vstup get 854
id get 1102
vstup get 1102
id get 1103
vstup get 1103
id get 1152
vstup get 1152

I know that on each page load values are set to default. This is for debug only. What I dont understand is, why setter and action listener is called only for last row (see output)


Thank you very much even for reading this :)

BalusC said...

You should place the h:dataTable inside a single h:form. You should not place h:form in h:column, this will render multiple form elements in each row.

Michal said...

Yeah! It works! I was bumping my head of it til Monday.

So I understand that h:form tags should be out of h:dataTable scope, not otherwise.
Does it matter if I have more forms in one html page? What about one form around all my page body content?

I guess I should read generated html code, but my knowledge of it is just very basic.

Thanks a lot again, man!

BalusC said...

You can have as many as you want of them, as long as you don't nest them (which is just prohibited by HTML spec, not by JSF spec). And in case of a datatable, obviously place the datatable inside a single form; each input component inside a table must know of each other.

Michal said...

Hi again, one last question.

I got through generated pages code.

There are no "recursive" forms. Thats why I dont understand why h:form in h:column dont work, as values from different rows dont have to see each other.

Moreover I read somewhere that html form in table is ok as long as all of is nested in one column. It also seems to me more natural - one form for each row.

Is that not an issue of JSF than?

P.S. I'll do it like you wrote but I'd like to understand reasones.

tc said...

Hi Balusc

Thank-you very much for your detailed article on using datatables - it seems like you are the leading authority on JSF out there in webland at the moment.

My question relates to the use of getRenderResponse to determine whether or not to reload the data. E.g.


public List getDataList() {
if (FacesContext.getCurrentInstance().getRenderResponse()) {
loadDataList(); // Reload to get most recent data.
}
return dataList;
}


I am using JSF within a portlet on IBM's Websphere Portal.

Unfortunately within within this environment getRenderResponse always returns false. I'm not sure whether this is linked to the Portlet life cycle.

Do you have any other suggestions for preventing unnecessary loads of data from the database the user selects an item, but also maintaining the most recent data when the user displays the list of items.

Regards

tc

BalusC said...

@tc: please continue here: http://forums.sun.com/thread.jspa?messageID=10395497.

dana said...

Thank you for the great tutorial.

I would like to edit a table's item in which one of the values should come from a "h:selectOneListbox" and not an "h:inputText".
How Can I do that?

Thank you,
Dana

BalusC said...

@dana: just replace the 'h:inputText' by 'h:selectOneListbox'. The 'f:selectItems' or 'f:selectItem' values can come either from the backing bean (if the values are the same for listboxes in all rows), or from the row object (if the values are specific for every row).

Jeremiah said...

this article was great! It helped in a very efficient way.

As a newbie in the topic, one thing I missed was including h:form around the datatable. It was only when I looked at the code for adding the edit functionality (later in the tutorial) did I notice that you were actually operating in the context of a h:form.

Thanks again

The Matrix said...

Hi, the blog was extermly very useful. But i would like to suggest you that for 'Populate dynamic datatable' it would have rather been correct if you had used 'i' instead of '0' in the for loop i.e.for (int i = 0; i < dynamicList.get(0).size(); i++)->for (int i = 0; i < dynamicList.get(i).size(); i++)

BalusC said...

The code assumes that every row has the same amount of columns.

ravi said...

Hi, Balu how to refresh datatable in jsf for every x minutes, the System retrieves data from database

BalusC said...

Add a meta refesh tag to the HTML head.

vijay said...

BalusC you are the best...

hemant said...

Thanks balusc for this code,
I tried to use section "Populate dynamic datatable" and it was very useful.
But, when I was checking the code on my side I was not able to display rows dynamically by your code.
I added one line to set the value of datatable dynamicDataTable.setValue(dynamicList);
and it worked Great!!!

Viper said...

Excellent and useful tutorial!!

Osian Hedd said...

This is a superb tutorial, and it has given me good insight into creating nice and dynamic components in the world of JSF.

The one problem that I have, is very similar to the one noted by Stef, but unfortunately, in the component, I do not know the parent bean. Let me explain quickly, I have a facelet component whom has a parameter passed in, lets call it 'controller', and this controller has the method to create the dataTable,

<h:dataTable binding="#{controller.dataTableImplementation}"/>

but when I set it on the binding, I get the same error as steff, i.e.

javax.el.PropertyNotFoundException: /test-component.jspx @14,67 binding="#{controller.dataTableImplementation}": Target Unreachable, identifier 'controller' resolved to null.

Is this concept possible with a facelet component?

Any help greatfully appreciated,
Thanks,
Osh

Manzoor said...

Hi,

Your tutorial is very helpful. Can u publish an article on how to dynamically generate JSF components based on permission/previlige of the user.

Manu

Maryam said...

It's was one the best tutorials I've ever met!
good on you:)

Peter Kovgan said...

Great Post!!! You are the GOD of JSF!

gen548 said...

Please correct me if I'm wrong.

Your code is working if the bean scope is session. The session scope, however, could lead
to the memory problem if a huge amount of data is sitting in session beans.

This is why request scope is perferred.

When using request scope, your code is not guaranteed to be working.

With request scope, you'll have to load data list from database twice.
If the data list you got at the second time is not exactly the same as at the first time,
then
JSF can not bind data table correctly and editDataItem() is screwed up.

In the worse case, if at the second time, no data list can be found in the database, editDataItem() is not even get executed.

This is a big problem with JSF when request scope is used. One has to somehow cache the d
ata to make the datatable binding possible.

BalusC said...

For that, use Tomahawk's t:dataTable instead. It works the same, but it has under each one important extra attribute: preserveDataModel. If you set it to true, then the datamodel will be cached for the next request and it will remove the potential problems you mentioned.

Deepak said...

Thanks for a great tutorial BalusC. However I think, in the code for MyBean Class, the line:

private MyDataDAO dataDAO = DAOFactory.getMyDataDAO();

needs to be replaced by the following lines:

DAOFactory DAOFactory = DAOFactory.getInstance("javabase");

private MyDataDAO dataDAO = DAOFactory.getMyDataDAO();

BalusC said...

Does in fact not really matter. It's all about the idea :)
But a purist as I am, I have changed it.

Prasad said...

Great! This is of Great help.I had struggled alot with datatables.This has solved many of my problems.
Thanks Again.

Santha Perian said...

Awesome! Lot of insight into the building of dynamic tables. Thanks.

I am actually building a dynamic datatable where Checkbox, Dropdown, etc., are randomly sprinkled into the cells. I hope your examples should help me more!

BaYRaK said...

heyy its owesome thanks for ur work..

cheachwood said...

A big Thx from France "you saved my life" for this work. ;-)

JP Singh said...

Thanks for the fantastic piece of dataTable and the debug JSF lifecycle work.
I had a question regarding commandbutton/link placed inside the datatable.
I had a look at your code - add_action_to_every_row.jsp & populate_action_datatable.jsp and the backingbean i.e. MyRequestBean.java.

I have this bean as request scope. I can display my datatable with rows having commandLink. If I click on any of the links, event is triggered in my backing bean if i have the following lines in my getDataList() method.

if (FacesContext.getCurrentInstance().getRenderResponse()) {
loadDataList(); // Reload to get most recent data.
}

But if I remove these lines my event is not detected and nothing happens in my invoke_application phase.

Can you explain this behavior? Why is my event not getting queued?
Please feel free to ask questions if I'm not clear.

Thanks very much.
JP

JP Singh said...

Apologies..Just want to correct something in the previous post. It works if the getDataList() has the following code:

public List getDataList() {
loadDataList(); // Preload by lazy loading.
}
return dataList;
}

Doesn't work if I have this condition
(FacesContext.getCurrentInstance().getRenderResponse())
Sorry about that.

BalusC said...

The getter is also called during apply request values phase.

The renderResponse trick is only applicable when you're using a session scoped bean. Also see the text in the article.

JP Singh said...

Thanks for the quick reply. I understand that.
But I'm afraid I'm still not clear that if I remove the loadDataList() call from within the getDataList() method, my event is not detected and nothing happens in my invoke_application phase.
My design was to call the loadDataList() when my event is triggered and the backing bean method is invoked. Issue is event is not being queued.

Can you please explain this behavior? Why is my event not getting queued?
Thanks
JP

Brendan said...

Balus - Thank you! Great info on the multi-select checkboxes within a data table. Nice stuff.

Sekar said...

Hi Baluc,

Thanks a lot for your support on this. Will this "populating dynamic datalist would work with JSF 1.1? I am restricted to use only JSF 1.1 and I am getting an error at the "createValueExpression stating this method does not exist". Can you let me know your inputs.
Thanks
Aisg

Sekar said...

Hi Baluc,

In continuation with my earlier post, I found the helper method private ValueExpression createValueExpression() and included in my code. Unfortunatly, It is erroring out in ValueExpression, getExpressionFactory, getELContext, etc. When I included javax.el.ValueExpression, it is not taking it and showing an error. Can you please let me know what I am missing.

Thanks
Aish

BalusC said...

Either upgrade to at least JSF 1.2 or make use of the JSF 1.1 ValueBinding methods.

Sekar said...

Hi Balu,

Thanks for getting back to me. When you say "make use of ValueBinding method of JSF 1.1", how do I do it? I changed my faces-config.xml to refer to version 1.1. I checked my .jar files and they seem to be of the same size as JSF 1.1 jar. Please let me know what else I should do to use valueBinding method of JSF 1.1?

Thanks
Aish

BalusC said...

Start with reading the javadoc of Application#createValueBinding().

Sekar said...

Hi Baluc,

Thanks for informing me. I looked at createValueBinding in javax.faces.application.Application and it says that this method is deprecated. Please let me know

Thanks
Aish

BalusC said...

So? You're using JSF 1.1, not JSF 1.2. It is just deprecated since JSF 1.2.

Sekar said...

Hi Baluc,

I modified the code and it worked fine. Thanks a lot for your code and your support.

What if I have a long list of columns to be displayed in the table. HTML Table may not be enough as we need to scroll 9both vertically and horizontally) to see all the columns. Can you please guide me so that I can try the same.

Thanks
Aish

Marius said...

What if we have the situation in which in one browser window a datatable row is deleted and in another browser window the same row is to be modified, what will happen in that case? Is there a way to place a message saying that the row is already deleted?

thanks,
Marius

Tshadi said...

Hi BalusC,

Thanks for the great Block it helped me alot since I am a newbie with JSF.

I used the selecting multiple example with a checkbox for my table and I also added the paging at the bottom of my table.

Everything works fine, except I get a NullPointerException if I select a few lines and don't page thought the whole table.

How can I resolve. Basically if someone wants to select rows only in page 1 and submits my form it doesn't work.

Thanks for your help.

Marius said...

@Tshadi

I had the same problem.
Had a few pages displayed and wanted to bulk delete a few rows. It didn't worked because the code says:

public String getSelectedItems() {

// Get selected items.
selectedDataList = new ArrayList< MyData >();
for (MyData dataItem : dataList) {
if (dataItem.isSelected()) {
selectedDataList.add(dataItem);
dataItem.setSelected(false); // Reset.
}
}

// Do your thing with the MyData items in List selectedDataList.

return "selected"; // Navigation case.
}


as you can see:

for (MyData dataItem : dataList)

that means that you'll itereate trough the whole list of objects. when the dataItem will go over the rows displayed you'll get the NullPointerException.

You need to identify what are the rows displayed currently.

What I did was:


List< MyData > batchRows = new ArrayList< MyData >();

for (int i = this.dataTable.getFirst(); i < (this.getDataTable()
.getFirst() + (this.dataTable.getRowCount() - this.dataTable
.getFirst())); i++) {
batchRows.add(this.< yourOriginalListOfObjects >().get(i));
}


then you retry the code as :


for (MyData dataItem : batchRows) {


should work ...

Tshadi said...

Hi Maruis,

Thanks for the reply, but it still craches in the for loop:

for (Parameter batchrow : batchRows) {
if (selectedParamIds.get(batchrow.getId()).booleanValue()) {
selectedParamList.add(batchrow);
selectedParamIds.remove(batchrow.getId());
}

olivier.hallot said...

Exellent tutorial, very ellucidative. Thank you so much for giving so much details and samples.

Just a glitch: the download of the war file seem broken. Sad.

BalusC said...

The download works fine and the WAR extracts fine. Maybe you're on a company network and its firewall is blocking file sharing sites.

Mo said...

Thanks. The info regarding checkboxes selection really helped.

Sander De Vos said...

Bedankt voor de grondige uitleg over deze component, het was heel nuttig en leerrijk binnen ons project!

JP Singh said...

I had question about the multiple row selection using checkboxes in dataTable.
I went through this example.
http://balusc.blogspot.com/2006/06/using-datatables.html#SelectMultipleRows.

This works fine for session scoped backing beans.
Just wondering if the backing bean is in REQUEST scope and not session scope, how can I retrieve the selected rowIndexes. Please advise.

BalusC said...

It is designed for request scoped beans. Maybe your data loading logic is wrong.

JP Singh said...

Thanks for the reply BalusC, I'll have a look again at my data loading logic code.
I really hope you are right.

Cheers
JPSingh

JP Singh said...

Hi BalusC,

Just had a quick question regarding the following method

FacesContext.getCurrentInstance().getRenderResponse()

In the getter method of my backing bean I want to distinguish between apply request phase and render response phase so i'm using
FacesContext.getCurrentInstance().getRenderResponse()

But it appears it does not return TRUE for all requests.

I have got the LifeCycleListener configured for an application. In my logs I can see

LifeCycleListener.BeforePhase: RENDER_RESPONSE 6 and then it goes into one of my getter methods...

public List getPaymentDetails() {
if(FacesContext.getCurrentInstance().getRenderResponse()) {
JSFUtil.debugInfo("InvoiceSearch.getPaymentDetails - Render Response Phase");
//Response phase logic
} else {
//Apply request values phase logic
}

My understanding was this method will always return true if in Render response phase...but i'm afraid it's not the case

Can you please advise what I'm missing here.

Thanks
JPSINGH

BalusC said...

Upgrade your environment. I've seen cases where in an outdated IBM portlet application with an outdated JSF version would incorrectly return false in getRenderResponse() all the time.

JP Singh said...

Thanks for the reply. I am currently using JSF RI 1.1_02 on TOmcat 5.5.

For some reason i cannot upgrade to jsf 1.2, can you please advise any other way of checking the phase?

Thanks
JPSINGH

tom said...

Thanks a ton for the article. Is it possible to create such a datatable using jsp alone without jsf?

BalusC said...

Use JSTL c:forEach.

tom said...

thanks for replying, BaluSC. if i use c:forEach how do I name the form fields and transfer values them to database. Any inputs would be appreciated.

BalusC said...

Use varStatus to get the loop index, use names with array index and use getParameterValues().

This has nothing to do with JSF nor with this blog article. Please use the appropriate places/forums for more detailed answers.

JP Singh said...

Hi BalusC,
Another question regarding dataTable

I'm doing a search by calling a webservice that returns me a list or orderVos. DataTable displays the list as rows. Each row has a Approve and Reject button, on pressing either invokes action methods.

So far so good.

Now an additional requirement is that the user can add some comments before approving or rejecting. What i'm doing now is that i have a textarea component hidden (display:none) in each row. Looking at the html source that gets generated the ids for the
textareas is 'j_id_jsp_1908125534_1:dataTable:2:paymentCommentArea'. I can retrieve the rowIndex and make up the id as 'dataTable:rowIndex:paymentCommentArea' and what im doing is

Iterator i = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterNames();
while(i.hasNext()) {
String s = (String) i.next();
if(s.indexOf("dataTable:" + rowIndex + ":paymentCommentArea") != -1) {
System.out.println("EXACT Key Found [ " + s + " ]");
System.out.println("trying to get the value ["+FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(s)+"]");
}
}

This works fine but just wondering if there's a better way of grabbing the textarea value..like can i bind it with
the backing bean....instead of looping through the request params.

BalusC said...

Bind textarea value to row object?

JP Singh said...

Thanks for the reply.

The dataTable rows are bound to the list of OrderVOs returned by the web service.
To bind the textarea i have to add an extra property to the OrderVO...i was wondering if i can get away without having to do this.
Any other advise will be appreciated.

Cheers

BalusC said...

Add the property to the parent managed bean then. Use Map<RowIdType, TextAreaValueType>. You can get the idea by looking at "Select multiple rows" chapter more closely.

JP Singh said...

Thanks for the advise, i'll certainly have a closer look to it.

Cheers

tom said...

Thanks Balusc for sharing your knowledge with us. Just 6 years of java and you have such amazing expertise, it's unbelievable dude. Keep up the good work.

skalluraya said...

Hi BalusC,

I have a problem in using Datatable for my reqirement. I want a datatable whichwhich requiresme to have dynamic columns. The number of colums to be displayed is to be fetched from server. How can i do this. Please reply

BalusC said...

Read chapter "Populate datatable".

Minam said...

I am having an issue on the radio datatable select option. I did the following:

h:selectOneRadio onclick="dataTableSelectOneRadio(this); submit();"
valueChangeListener="#{pc_Selectaccount.setSelectedItem}"
f:selectItem itemValue=""
/h:selectOneRadio

But it keeps iterating through the entire datatable and executing the valuechangelistener. But when I actually select a button, it doesn't run the valueselectlistener on the button I clicked, it just rereuns it on all the data again. In this way, my currently selected radio is always the last row. Any help? Thank you!

Bern said...

Hi,
I am trying to build a validator to check wether at least one row is selected via check box.
My approach is similar to the second way to select multiple rows using bindings (via a Map(String, UISelectBoolean)) for the checkboxes in order to access their values in an validator (attached to a button).
Doing so I run into the following issue:

h:dataTable value="#{myBean.itemList}" var="item">
h:column>
f:facet name="header">
headerText
/f:facet>
h:selectBooleanCheckbox value="#{item.selected}" binding="#{myBean.checkboxBinding[item.id]}" />
/h:column>
...
/h:dataTable>

Gives me a PropertyNotFoundException: binding="#{myBean.checkboxBinding[item.id]}": Target Unreachable, 'BracketSuffix' returned null

The only explanation I have is that binding expressions are evaluated before the dataTable loops over its rows, hence there is no "item" during binding evaluation.
Any hints on how to get this binding working (or another way to validate the checkboxes)?

Thanks a lot!

BalusC said...

The binding="#{myBean.checkboxBinding[item.id]}" isn't going to work. There's only one JSF component. Replace by binding="#{myBean.checkboxBinding}"

Bern said...

That brought me one step further ;-)
How can I now access the values for the different rows in the one component?

Tanks a lot for your help!

Bern said...

And maybe you can point me to some good documentation / tutorial regarding the internals of the dataTable. I guess I am struggling with this because I haven't found any yet (besides your great tutorial regarding dataTable usage of course ;-).

BalusC said...

Use its 'value' attribute.

kamaraju said...

Can u please explain how to use column and row span in datatable. Please can u provide sample piece of code

BalusC said...

Not possible with h:dataTable. Colspans are possible with Tomahawk t:dataTable. Rowspans are only possible if you generate your own HTML table using t:dataList or ui:repeat (or c:forEach).

amita said...

Hi BaluC,

I am in a catch 22 kind of situation. I am using rich:datatable to display a couple of rows. I cannot assign ids to them because they are looping through a data structure from the bean. My funtionality is to submit each row independently. So here comes the issue: I need to have the bean in session scope for the actions menthods to be called from a a4j:commandLink inside my rich table. But since the scope is session, my ids are not getting refreshed and I am getting an duplicate id error.

If I solve this issue by having the session to request, my action methods are not getting called from within the rich datatable.

I tried issuing ids to the rows by using jstl but they are getting ignored during rendering phase.

Can you please advise on how to solve this situation.

fluxion said...

Hi Amita,

I believe you have some kind of command button or link called as 'Submit' on each row of the dataTable. If you try wrapping your collection in ListDataModel we would actually get hold of the row/object the is button is clicked, in the action method.

Regards
-hm

amita said...

Hi Fluxion,

The problem is that the action method doesnot get hit in the request scope.
Otherwise, in the session mode I am able to uniquely identify the user selection.

Rajesh said...

Hi BalusC,

I have a requirement to populate dynamic datatable based on value selected from drop down.

step 1: User will select name of table from drop down. No submit button is there.

step 2: Using Ajax, dynamically data table will be populated.

What I have done :


// Using rich faces ajax support
start h:selectOneMenu ....
.....
a4j:support event="onchange" reRender="dynamictableList"
.....
end h:selectOneMenu

then using similar code as BalusC code for dynamic data table (populated from backing bean) :

h:panelGroup binding="#{userLoginSuccessBean.dynamicDataTableGroup}" id="dynamictableList"

But the panelgroup with id="dynamictableList" is not re-rendering in JSF page to fetch new values.

Where as other UI components are getting re-rendered when used with a4j:support.

Any solution would be really helpful.

Thanks

Sudeep Agrawal said...

Hi BalusC,
I have been working with your example .war code for the DataTables with the Radio Button. The ValueChangeEvent is not fired without a submit(), which is missing in the explanation / Javascript function dataTableSelectOneRadio(). Is this True or am I missing Something ?

BalusC said...

The Javascript does indeed not submit the form immediately. If you want to submit the form immediately (so that the ValueChangeEvent at the server side goes immediately fired), then you need to add a JavaScript line which submits the form.

alex said...

Great post thanks! could you help me with setting progmaticaly addActionListener to HtmlCommandLink in datatable.I did idLink.setActionListener(facesContext.getApplication().createMethodBinding("#{myBean.processAction}",null));
idColumn.getChildren().add(idLink);

but it does not work. says Method not found.
Thank's.

BalusC said...

You forgot to specify the ActionEvent method parameter in createMethodBinding.

If you're using JSF 1.2 better get rid of the deprecated methods and use createMethodExpression and MethodExpressionActionListener.

alex said...

Thank you for a tip. I got the full working code like this:

FacesContext facesContext = FacesContext.getCurrentInstance();
Application application = facesContext.getApplication();
ExpressionFactory el = application.getExpressionFactory();

MethodExpression methodExpression = el.createMethodExpression(facesContext.getELContext(), "#{myBean.processAction}",
null, new Class[]{ActionEvent.class});
MethodExpressionActionListener methodsexpression = new MethodExpressionActionListener(methodExpression);
idLink.addActionListener(methodsexpression);

alex said...

Hello again.
I still in solution search.
I thought addActionListener with action listener method
HtmlCommandLink hc = (HtmlCommandLink) getTable().getChildren().get(0).getChildren().get(0);
System.out.println("value cell: " + hc.getValue());
will help me to get cell value but I get null instead of cell value.

I have anagrammatically generated datatable of HtmlCommandLink's and need to detect which link was pressed by user, get link value (cell of datatable) and send him to next page with appropriate settings.
GetTable().getRowData() gives me whole row data what is not good in this case.

petar said...

Thanks for your article. It is still actual after many years you wrote it.

It should be "must read" for all JSF developers. There are many experienced ones who always have need for editable data table, as the matter of fact, JSF frameworks should start offering some nice css designed skinable datatable instead creating over and over custom solutions.

Thanks again.

J├ęssica said...

Hi!
I don't know how can I open the war or use it to view your examples. What should I do with this file?
Thanks!

BalusC said...

It's just a Webapplication ARchive. It's just a complete Webapplication file structure in ZIP format. You normally deploy such files in the application server.

Dey said...

Hi BalusC,

I am having problems with having selectOneMenu inside datatable and setting it's default value.

What I want to happen is that when my "EDIT ROLE" page loads, datatable will consist of selectOneMenus depending on how many rows the page returns based from roleId. The selectOneMenu is dynamic, as well as the selectItems. Data comes from DB. The datatable loads properly if I don't set value for each selectOne menu.

To illustrate:
FUNCTIONS VIEW ADD EDIT DELETE
#selectOneMenu>with setValue #checkbox> #checkbox> #checkbox> #checkbox>#selectOneMenu>with setValue #checkbox> #checkbox> #checkbox> #checkbox>#selectOneMenu>with setValue #checkbox> #checkbox> #checkbox> #checkbox>#selectOneMenu>with setValue #checkbox> #checkbox> #checkbox> #checkbox>#selectOneMenu>with setValue #checkbox> #checkbox> #checkbox> #checkbox>

This table can be edited that's why I needed a drop-down (selectOneMenu). I hope you may be able to assist me. Thanks!

BalusC said...

You forgot to tell the actual problem.

If your actual problem is "I do not know how to set selectOneMenu's default value.", then the answer is simple: just preset the property behind the 'value' attribute.

Dey said...

Hi BalusC,

I know how to set the default value, problem is, when I set default value of selectOneMenu, it doesn't appear inside the datatable. In short nothing appears.

I've been googling this past 2 days about putting selectOneMenu inside datatable, and it seems that most people are having the same problem. Another question is, what kind of approach would you suggest to display selectOneMenu per row on a datatable. Thanks in advance!

BalusC said...

I have no idea about those problems. Most likely the code flow / data loading logic is plain wrong. Just debug it or post a topic with an SSCCE at a JSF forum (forums.sun.com maybe?).

Nikolai said...

is there any way to get data from cell i clicked on? i tried to use a4j:support inside rich:column but it didn't worked. It didn't fire an event at all. But even if it had fired an event i wouldn't have known what to do with it. Because i don't know how to take cell's value inside backing bean. :)
For example, if i want that a click on a cell would call some action that would take a value of this cell and printed it in console. How can i do this?

Anand said...

Thanks Balu. this article has been a tremendous help.
- Anand

Boros said...

I want to use checkboxes in datatables and I'm considering your second approach with

<h:selectBooleanCheckbox value="#{myBean.selectedIds[dataItem.id]}" />

I see there is a

private Map<Long, Boolean> selectedIds = new HashMap<Long, Boolean>();

in MyBean but it has only a getter method, no setter. What happens when you submit the form? How does the information get into selectedIds which is a private member?

BalusC said...

In EL, there is no setter needed for collection and map properties. EL just does bean.getList().add(index, object) or bean.getMap().put(key, value).

Ramesh "Defeat the Defeats;Defeat defeats you" said...

Hi balusC,

I need radio buttons inside
the datatable(JBoss & RichFaces).
How to create rows inside the dataTable? Post me as soon as possible....i ve to complete by tomo..

thanks in advance...

Brian said...

Hi Balus,

Great blog. One question regarding your "actionSearchReset()" in your crud.jsp example. To reset the search parameters, all you did was searchExample = new MyData();

However, when I try doing that the search parameters still remain. In order for me the clear those values, I had to set null to the submittedValue for those components.

Am I missing something here? Is there something else I need to do. I am new to JSF and I get really confused as to when you use the component i.e., HtmlInputText skuInput versus a simple getter/setter value in a POJO like private Long sku.

Your help would be much appreciated. Thanks again for the blog.

BalusC said...

It is not clear to me if the problem in CRUD example or in your own webapp. At least the CRUD example works fine here.

With regard to component vs property: component binding is useful if you want to do a more than just getting/setting the value as bean property. In case of HtmlInputHidden, the value will survive the complete JSF cycle regardless of the outcome of validation and conversion. This is important to pass the value from request to request in a request scoped bean. This is also explained in the article's text. Just carefully read it and do not only focus on the code.

Brian said...

Thanks for the reply Balus.

I realized what I did wrong. For my clear button, I set the immediate attribute to true. When I set it back to false, the clear button now works like yours.

However, there's still one problem, which was the reason why I switched it to immediate equals true. The reason is this. If you enter a non-numeric value for Sku, JSF will do it's validation first. As a result, it will error out and not continue with the clear.

So what I did was, set the button action to immediate equals true. In the backing bean, I cleared out the submitted value for the parameter component I wanted to clear out.

In your opinion, what is the best way to implement a clear action where all the user wants to do is to clear out the input parameters regardless if there are any validation errors inside those parameters.

So in this case for search parameters, would it be better to map those parameters to an HtmlInputText component value versus to how you are doing it now? And then in the backing bean, you would have a clear method that would set the submitted value to null for those components? In the jsp, you would map the value to [component].value?

Thanks again.

BalusC said...

The immediate="true" on the button (and not having it on the input components) only prevents the input values being set. When response is to be rendered, JSF unfortunately retrieves the input values from the components itself, not from the bean.

Validation and conversion in JSF is hard to skip, indeed. The control is only limited to the required attribute. Easiest way is to separate the reset button in its own h:form.

Boros said...

I have put checkboxes in a datatable using a HashMap, but it only works in session mode. Trouble is that my datatable depends on what I select from a selectOneMenu element, and I think that the selected value is applied too late in the JSF lifecycle so the checkboxes are not correctly bound to the HashMap. How could I keep my bean in request scope?

Boros said...

I managed to make it work inserting this in the bean's constructor:

HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
selectedInstanceId = request.getParameter("logsform:selectedInstanceId");


It works but isn't there a better way to do this?

qinggong321 said...

BalusC,
Thank you sooooo much for your article. It helped me a lot for the JSF work I am doing. It is the best article I have found online. Please don't remove it. People will refer to it again and again, I am sure.

akshar said...

Hi Baluc,

Greatest Artical, well described and much appreciated one .

It helped me to have sorting dataTables in my application.

Using your sorting method i am facing a problem . i have 'storageBean' named bean class and from that bean class i am getting 'nsList' as List to be represented on dataTable.Problem is that on header of the dataTable some one clicks it calls getNsList() twice rather once.

do you have any idea am i missing something which is causing this?

i might not provided you full information to understand the problem , please let me know what else you need to understand it .

Thanks in Advanced.
Bhargav.

BalusC said...

First call is done to get original input values and the second call is done to display current values. That shouldn't harm. Getters are just there to return data. Read "Debug JSF lifecycle" article for more details.

merve said...

hi, i tried to display my data from a database using an arraylist as a managed propertyand using a h:datatable but it repeats like

apple
orange
nut
apple
orange
nut

in the jsp page :S

what should i do now?
thanks in advance.

BalusC said...

Sounds like you wrote data loading logic in the getter instead of the bean constructor! I suggest you to read the "Retrieve and store data" chapter.

merve said...

hello,
thanks for urgent reply.
yes i wrote the load logic in getter method.
i will read that chapter.
thanks in advance.

merve said...

hello again,
i carried the loading logic to the default constructor with has no parameters and now it works fine. thanks.

qinggong321 said...

Hi, BalusC,
I tried to use your sortDataList approach to sort my table, but it didn't work, because if myBean is a request scope, the sortField is always set to null and the "if (sortField != null && sortField.equals(sortFieldAttribute))" is never invoked, and sortAscending is aways set to true in the "else" block, correct?
Can you tell me what I did wrong?

BalusC said...

Likely you forgot the h:inputHidden elements. Check the code example once again.

qinggong321 said...

BalusC,

You are probably right, I may have forgotten "h:inputHidden value="#{myBean.sortField}"
and "h:inputHidden value="#{myBean.sortAscending}" "
I will try again. Thank you so much!

qinggong321 said...

Hi, BalusC,
Somehow the FacesContext.getCurrentInstance().getRenderResponse() never works for me to prevent duplicate data loading. If I use this condition in the getter, my data will never get loaded. I am using JSF 1.1. When I use the lazy loading in my getter, the call to the DB still gets executed multiple times during each request. This is the most annoying part of the JSF. I don't know what would be the best way of preventing it. Any advise would be greatly appreciated.

BalusC said...

The problem lies somewhere else.

As to the data loading, the constructor/initblock is the best place, not the getter.

Anand said...

Namaskar Balus, Thanks for a wonderful article that has clearly withstood the test of the greatest opponent; time ! You are an inspiration.

Bhishma.

kjs said...

Hi Balau C,

When i tried to edit the valus in the data table and then save only the first row of the table changes alone getting reflected. Remaining rows are coming as it is.

Please find the code below,

dataTable
id="fr"
value="#{forecastTableBean.list}"
renderedIfEmpty="false"
var="Forecastlist"
bgcolor="#F1F1F1" border="10" cellpadding="5" cellspacing="3"


Please help me to fix this issue.

BalusC said...

Editing/saving a datatable is already covered by this article.

kjs said...

I floowed the same approach but only the first row changes is getting reflected. I dono what changes needs to be done to make all the rows gets changed with new value.

When i print from FacesContext request parameter I am able to see all the values but datatable list is not getting updated with the new value.

please provide me some idea to fix this issue.

Thanks in advance.

regards
Senthil

badshah said...

Can you please post the same using rich:datatable, using f:setPropertyActionListener, a4j:support

with backing bean code.

This was really helpful but I am using rich:dataTable.

schnegge said...

Great post, great blog.
Tried sorting my dataTable today.

Might there be a little mistake in your DTOComparator? Exchange o2 and o1 in last else-block of sort()?

All the best. I will definitely return to your page.

aby said...

JSF>>>>>> h:dataTable and h:commandLink not working together

is there any solution for this i have tried some methods
like changing the "managed-bean-scope" to "session" but with little
improvement, the page refreshes itself,any solution, Suppose if i am
executing a querry its resultset has more than 1 result to return, is
this data table the real solution or i have look for some other
method(because i want to link this result(each result) to some other
page), Any Suggestion


but when i am using c:foreach i am not able to pass the value of the field to bean

aby said...

How to call a action on page load in jsf


i am having a JSF page where i want to display the records from the datatable. I have a method in bean which call database and gets the list of records. If i call this method from a link (action on this link) present on this page i can display the records on this page.
But i want to display these records at the time of page load only. How we can do this? How to call a action at the time of page load?
Thanks in adv.

BalusC said...

Use bean constructor or init block, as already mentioned in this article.

Maurer said...

Hi,

this post was very helpful for me, but I have still one small problem, that drives me nuts.
In "select multiple rows using checkboxes. ":
I used the second solution, as I shouldn't change the Object.
No matter what I do I always get the values back as false.
So I check in the checkbox in a few rows. Click on the cmd button and get the Map back, but all the values are set to "false".
Do you have an idea, why doesn't that change?

thanks.

BalusC said...

Either keys are non-existing or model value phase has been skipped for those values. Debug it.

Ram said...

Hi BalusC,




I am in need of creating dynamic datatables, which I've accomplished, with the help of this this post.
Also, I have some buttons, associated with the datatable.

Now, I need to copy - paste the entire datatable (with all its properties) on an action, which can be done as many times as possible.

Is there any method to copy the object which is already enclosed within a container (eg: panelgrid) and replicate it there itself ?

«Oldest ‹Older   1 – 200 of 297   Newer› Newest»