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

317 comments:

«Oldest   ‹Older   201 – 317 of 317
Unknown said...

FacesContext.getCurrentInstance().getRenderResponse() doesn't work for me for request scoped backing bean either.

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

Hi BalasC,
In dataTable i have 6 columns
In that i want to compare 4th and 5th column values.In 5th column i have InputText Box.The validation is such that value in the 5th column should less than the value in the 4th column value for a particular row

Below code was working only for the last row of the table. how to apply validation for rest of the rows in the table.

In JSP Code,


h:inputText styleClass="txtBoxMediumRight" id="Approvedamount" value="#{dataItem.tapproveAmount}" required="true" validator="#{billerApproveCreditRequest.validate}"
a4j:support event="onblur" ajaxSingle="true" /h:inputText


In Java Code,

public void validate(FacesContext facesContext, UIComponent uIComponent,
Object object) throws Exception {
double enteredAmt = (Double)object;
System.out.println(enteredAmt);
dataItem = (ApproveCreditRequestData) table.getRowData();
dataItemId.setValue(dataItem.getId());
for (ApproveCreditRequestData info : dataList) {
if (info.getId() == dataItem.getId()&&(enteredAmt>dataItem.getTrequestAmount())) {
FacesMessage message = new FacesMessage();
message.setSummary(" * Always approved amount should be less than requested amount");
throw new ValidatorException(message);
}
}

Unknown said...

hi Balusc,

Many thanks for your wonderful post regarding dataTable. Could you tell me how can i get a dynamic column in a dataTable(on clicking a checkbox in a popup and hit submit the page should be refreshed with some new column .Iam using tomahawk t:columns tag . but i don't really understand it's usage.Any input you have is highly appreciated.

Thank You

Unknown said...

Many thanks for your article. I am new in jsf and it helped me alot in dataTable concept. I need a help from you -
I will have to bean MyData1 and MyData2 for two pages (Data1.jsp & Data2.jsp). From page ones' dataTable i want to selects some lines using selectCheckBox. Those selected row will go to MyData1 bean and the result will be dispalyed in Data2.jsp . in Data2.jsp i'll per form some other actions which will be managed ny MyData2 bean. Please suggest ..
Thanks in Advance
Soutrik

Ryan said...

Hi BalusC,
I am using h:dataTable .I am trying to implement drag and drop facility for columns for the dataTable.Is it possible for h:dataTable or should I check out for any other alternative?

JP Singh said...

Hi BalusC,

When using datatables, i want to store the row number with each of the rows. I know if i bind with a HTMLDatatable and use the rowindex i can get it.

Couple of questions -
1. Is binding with HTMLDataTable(just for getting the row number) an efficient solution.

2. Just wondering if there's any other way of doing it.

Thanks
JPSingh

Eosphoros said...

@Ram - You can use the "binding" attribute to key your component to an Object of the respective type in your backing bean.

Unknown said...

Hi BalusC
in my data table I have 4 columns and 3 columns are getting data from three different List. 4th column is a checkbox. Now based on the selection i need to identify the row and retrive the data from each cell. Can you suggest something who to do that. Thanks in advance.

Unknown said...

Hi Baluc,

In One of my project,i have used sorting mechanism and data fetching mechanism the way you described in your post.it was really helpful.

i had put comment on before once on it.

Need your help to solve one problem.
i have page site.jsp which calls storageBean.java which in turn uses methods of storageSevice.java.
storageService.java has all DAO's injected as spring bean.

Means site.jsp -> storageBean.java -> storageSevice.java [which will get data from different DAO's]

i have created one error.jsp file.which will be called if any point of time any exception comes by "page errorpage=error.jsp" from any of jsp file.

Now Problem is ,
sometime error.jsp would be displayed if we click first time stating "Couldnot instantiate bean class ,storageBean constructor threw Exception:java.lang.NullPointerExcetion.

if i click on link secondtime this problem gets disappear and page loads fine.

please advice if you have any idea to resolve this problem.

Thank you
Bhargav.

Unknown said...

Hi Balusc

I have a requirement of using all binding varaibles. For some reason when using pagination binding variable am not able to get the pagination component on JSP page. Help me out.

Regards
Srinadh

ABY said...

i am using a " rich:editor " i want to limit the number of characters that can be entered into the editor ,is java script the solution or anyother method ,or any attributes of editor can do the work

please give me a reply

Markos said...

Thanks for this tutorial. It is great!

SomeGuy said...

Thank You, Great Article!

One question - In the dynamic column part of the article, Is there a way to add or remove column dynamically? (When the Page has already loaded with the table).

BalusC said...

Do a `this.table = new HtmlDataTable()` and repopulate it.

Java Lover said...

Hi,

Can you show me how to get rowIndex and forward to the next page after clicking that row from datatable?

Thanks,
Diep

BalusC said...

Use `f:setPropertyActionListener`.

Java Lover said...

Hi,

I tried your "Highlight and select row on click". It works perfectly. But my requirement is a little different. Instead of highlight the row and click on the button to go to the next page, i would like somehow just highlight the row then it directly go to the next page carrying the rowIndex. Can you please help me? Thanks for your codes and your help.

Java Lover said...

Hi BalusC:

I have tried to use your sample for Highlight and select row on click. The highlight works. However the
document.form.rowIndex.value = trs[i].rowIndex; ==> does not work (ERRORS)
What i did was i put the addOnclickToDatatableRows and highlightAndSelectRow in separate *.js file. I don't know what the right syntax to set the trs[i].rowIndex to my hiddenInput rowIndex. Can you help me? It has taken me 2 days to figure it out. NO LUCK.
Thanks so much for your help.

Deepthi said...

Soring datatable is worked for me!!thanks balusc..
In compare method ,in the last else block exchange o2 and o1...

EDH said...

Regarding var="dataItem".
Retrieving a value of a simple dataItem attribute is easy: dataItem.xyz.

What if the dataItem contains a Collection attribute ?
How would I access the first element of that collection ?
dataItem.???

Or are only simple attributes supported ?

Please advice.
Thanks
EDH

BalusC said...

${dataitem[0]} will return first element of list.

Mark said...

Hi BalusC,
This is great information ... thanks! I have one question, though, on the "populate datatable" section: If my backing bean makes changes to both the row data and the column structure (headers, widths, etc., for example), the data changes appear when I reRender the table from an a4j commandLink, but the column structure changes do not.

Do you have any thoughts on how I can get the reRendering process to through all lifecycle phases so that the table structure is rebuilt?

Thanks!

Robert

Unknown said...

Thanks u so much, I could able to get multiple row information in my bean after implementing the checkbox in datatable

Unknown said...

Thank you very veryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy and sooooooooooooooooooooooooooooooooooooooooooooooooo much. It was really a good help for me. I was looking for this information for along n i've found it here simple and easy to understand.. I really thank you from the deep of my heart

Joseph said...

Hi
Thanks for the great article. This is the first comprehensive article i have ever read on datatables. I am sure you can contribute your article on Netbeans wiki or java.net.

Sadly enough your article doesn't pop up on google with the datatable jsf keywords.

Unknown said...

hi : i have populated data table using rich:dataTable right now i am selecting a row by clicking and there is a button in top of the page called detail, on clicking detail button a new page is displayed having detais of that particular row. Now my question is i want to get same detail on DOUBLE CLICK of the row. how can i implement this?

thanks

Unknown said...

hi : i have populated data table using rich:dataTable right now i am selecting a row by clicking and there is a button in top of the page called detail, on clicking detail button a new page is displayed having detais of that particular row. Now my question is i want to get same detail on DOUBLE CLICK of the row. how can i implement this?

thanks

amdrill said...

hi balusc,

hi have tried your sample in creating nested datatable and it work great... I have a problem in my report and I believe you can help me to solve this. Please see my this link: http://www.coderanch.com/t/211729/JSF/java/Dynamic-dataTable-Using-JSF

Thanks,
amdrill

Unknown said...

Hi BalusC
I havde found your article extremely helpful, I just have one problem in what I am trying to do that you maybe able to help me with, I have a page that allows users to select query criteria and then on submit I want to populate the dynamic table and display on same page, My issue is that I dont have any lists the first time i render, is there a way to render the data table every time.

Unknown said...

Hi, Thank you for this extensive article

I have no success at the retrive data chapter because during a subsequent requst, I get a NullPointer exception, my bean is in request scope and have a reference to another session scope bean that return the list for dataTable,

I try to init it in constructor that give me exceptions because JSF have not yet injected the session bean that's associated with the request bean into the request bean. I separated the DAO task to the session bean.

I have tried to do the same task in the init of HtmlDataTable but than the method in the button still didn't get fired

Thanks for your thoughts.

Iron Yan said...

Hi BalusC, I'm currently using jsf and facing problem where the h:commandLink's action and actionListener are only fired after every second click. I'm using the h:commandLink inside a h:datatable which is inside a h:form.

I've tried setting the prependId of the form, and javax.faces.STATE_SAVING_METHOD, but nothing works, everything else inside the h:form and outside of the h:datatable works fine, do you have any tips or suggestion please?

Thanks
Ge

Unknown said...

Hi BalusC,
could you please give me advice how to create a link to a datatable row? I send out a daily newsletter to users and I would like to put a link to the end of the news in the email pointing to the proper news in the datatable. I use t:dataTable in a JSF page, enhanced with some A4J.

I would appreciate if you could give me a clue how you would do it.

Best regards,
Peter

Unknown said...

Hi BalusC,

What do you think about below way of moving variables from one backing bean to another?

public String update() {
FacesContext ctx = FacesContext.getCurrentInstance();
Application app = ctx.getApplication();

ValueExpression _sV = app.getExpressionFactory() .createValueExpression(ctx.getELContext(), "#{nextBB.variable}", YourObject.class);

return "update";
}

Best Regards,
Marius

à®°ாமகுமரன் said...

thanks a lot balusc , your notes abt radiobutton helped me a lot

Unknown said...

I have a question with respect to retriving values from a dynamic data tables. I have a data table that gets the values from a list. In the server side i have to retrieve the values of components in individual rows and I am able to do it by changing the rowindex of the HtmlDataTable object. But I also need to get the header values that are used that are defined for each column as a facet. I am able to retrieve the HtmlOutputText components by calling getFacet method on UIColumns, But the values are always null for these HtmlOutputText components. Please let me know if there is anyway i can retrieve the values of the headers also.

bouiks_blog said...

Hi balusC!!!I was looking for your ear file.i don't see it anywhere!!Could u please give me the link?

BalusC said...

It's a WAR file. You can find the download link in the last chapter.

kay said...
This comment has been removed by the author.
kay said...
This comment has been removed by the author.
kay said...

Hi I am trying to have HtmlDataTable similar to what you had in the dynamicDataTable example, but I need dataScroller for a long datalist. Would you tell me how can I do it?

I tried


but it doesn't work.

kay said...

Hi BalusC,

Thanks for your example, it helps a lot. I come up with the problem for rendered for the HtmlCommandLink which relative to the value from the valueBlinding.
i.e. show the link if the id is not there.

"#{empty dataItem.Id}"

setRendered is not suitable for my situation because my value is not known till render.

kay said...

Hi BalusC,

I have problem regarding dynamic datatable.

my Object is like
class dataItem{
int id;
String name;
List post;
}

class post {
String postname; ...
}
The following line works.
Output.setValueBinding("value", (ValueBinding)FacesContext.getCurrentInstance().getApplication().createValueBinding("#{myItem.name}"));

but this line doesn't when I tried to get the List object.
Output.setValueBinding("value", (ValueBinding)FacesContext.getCurrentInstance().getApplication().createValueBinding("#{myItem.post.postname}"));

question 2, is there a nice way to iterate the list post and put them as the same table?

my desire table will have column like this.
id | name | postname1 | postname2 |postname3 |..| and so on..

your early response will be much appreciated. >:{ (<-frustrated)

chris said...

I have the following problem using JSF with rich:datatable

I can render a button depending on a comparison with a row value and a session variable.

If var="row" is the row object and "row.value" the value I wish to compare with "myVar", the session variable, I would use the following EL-expression

rendered="{{row.value==myVar}"

This works fine.

Now, I would like to do the same thing but depending if myVar is contained in a list of values linked to the current row (row.list)

that would be

rendered="{{row.list.contains(myVar)}" which of course is not a valid EL-expression.

How can I work this out ?

Verma Dinesh said...

Hi BalusC,
Its very exciting that i could perform editing data showing from h:dataTable tag. Its very nice blog to be followed...
Thanks&Regards
Verma

B. said...

Thx for your examples which work well for me. In my dataTable I have one problem left. When the page loads no radio Button is selected. I have a list of images which populates the dataTable and want to determine one as the mainImage. In a pre render method I set the selectedImage and when the user choses another I use your example to store this, which works in the bean, but not in the UI. On first load and after reloading no radiobutton is selected.

.
.
.




Thx in advance! Babette

David Squire said...

Your article was very good! Thank you very much.

I tried to nest your



within a tag.

So my tag looks like this:



When I run the app, I get the following JSP Exception:

JspException: org.apache.jasper.el.JspPropertyNotFoundException: /s/UtilitiesTableSeg.jsp(52,56) '#{attributeTable.dynamicTableGroup}' Target Unreachable, identifier 'attributeTable' resolved to null

Do I need to do something to make the var from the tag available? I don't understand what's wrong because I can use the 'attributeTable' within other tags (like .

Thank you very much for any additional info you may have.

Dave

Geetha said...

hi sir,
your article is superb...
i m new to j2ee, lots of confusion between request csope and session scope. Can u please tell what s the differance in"usingDatatables"

Geetha said...

hi sir,
your article is superb...
i m new to j2ee, lots of confusion between request csope and session scope. Can u please tell what s the differance in"usingDatatables"

Nitin said...

Hi BalusC, i just want to know is it possible to have different session in same browser(i.e multiple tab in mozilla) using JSF ? requirement is something like this, user should open different accounts in one browser wid different tabs.

Marcius Brandão said...

hi Balusc,

as paging the nested DataTable?

Kapil Avasthi said...

Hi BalusC,

I followed your code for implementation of radio button inside datatable, i am using openfaces. below is my code for the xhtml & bean class.
I am facing two problem :
1. If i am selecting last row of table, than table allows me to select one more row(i.e. now i can have two radio button checked)
2. In the bean, i am getting the last row value always, regardless of the row i am selecting.
please help me where i am going wrong.





















public void setSelectedItem(ActionEvent event) {
log.info("AlertSummarySC | setSelectedItem | Start");
dataItem = (AlertSummarySC)dataTable.getRowData();
log.info("Inside the setSelectedItem"+dataItem.getAlert());
}

Regards,
Kapil

Unknown said...
This comment has been removed by the author.
Fize said...

Hi
Many thanks for your articles. They've been really useful.

I hope you'll be able to help me with this issue. I have a dynamic datatable written populated using Java. It has dynamic columns for each product size that is available. Some items are only available in some sizes. For those available I want to render a UIInput, for those that aren't I don't want to render anything.

e.g

Product Colour XXL L
prod1 Black Input no input
prod2 Red no input input


I'm having to populate this table in JAVA code.
However I can't see how to use EL to selectively render within the JAVA code, as the .setRendered method only takes a boolean and not EL, and if I use quantityInput.setRendered("#{!empty row.rowItems[" + j + "].item}"); this doesn't work. Are you able to help?

Unknown said...

Hi ,
Can any one help me.
I need to iterate Hashmap in jsf Datatable.
My hashmap looks like this
HashMap>.


I want to bind this hashmap to a datatable. And each value which is
an arraylist will be a table inside the main table.

Unknown said...

How is the dataItemId used? I don't see any references to it except for updating it. Is it used to lookup the item by it's id?

Unknown said...

Great tutorial BalusC!
I used your technique to build a datatable with some radio buttons inside it. Now, I want to select a default radio button when a user returns to the page based on what was selected earlier. I would appreciate your help.

Kaleem Khan said...

Hi I'm very new to this stuff, but at this point I need the total rowCount (not rows to display) in Grid. And then I need to use this value outside dataTable tag so variable would help. I have seen example with but I think it doesn't apply to JSF.

Any help would be appreciaed.

Kaleem Khan said...

Hi I'm very new to this stuff, but at this point I need the total rowCount (not rows to display) in Grid. And then I need to use this value outside dataTable tag so variable would help. I have seen example with but I think it doesn't apply to JSF.

Any help would be appreciaed.

Arun Jacob Elias said...

Hi,

I am following your example.
While working on select row using radiobutton I am stuck because I am getting the following error while selecting a row using radiobutton

radio.name is undefined
var id = radio.name.substring(radio.name.lastIndexOf(':'));


How can I resolve this error?

Unknown said...

Hi BalusC,

Thank you very much for this great article. I have an editable datatable but am not sure how to display the column level validation messages. The h:message for does not seem to work. In my WebLogic Console I see this message ... FacesMessage(s) have been enqueued, but may not have been displayed.

The only way I could display the error message is if I define the h:message inside the h:column. I would like to display the message at the top or bottom of the datatable. Is this do-able?

Please advise.

Unknown said...

hello balu sir,
I have a requirement on editable data table.
i have one editable data table with data ,down the table have link to populate same data table down to the 1st data table.like that when user clicks on that link it should create more than 100 data tables in a same page .(same editable table with same data).
Please help me regarding this.
Thanks and Regards
vinnu

open-mind-community said...

Hi BalusC,
I am working on jsf.I have to split the column dynamically in 2 columns and the second column in four rows on a particular value of a bean.
how it can be done.

thanks,

open-mind-community said...

Hi,
I want to split a column in h:datatable for a particular value of a bean.I have to split one column in 2 columns and then 2nd column in 4 rows.
how i can achieve this.
Thanks

Unknown said...

Hi BalusC,
I checked your Using datatables blog it is very useful for the development of jsf application.
Actually i have one scenario in that i have 5 columns with datatable name,id,qty,price and checkbox.the data is coming on the screen and below it is having 2 textboxs
i.e 1.Net Qty 2.Net price when i checked on check box of particular row it should calculated the total of all qty and price and displayed in the inputbox.
hint:it should be dynamical change when i clicked on one more checkbox.

i want to use openfaces with jsf2.please help him out with this.

Mani said...

How to generate from backing bean and add it as a child to outputtext?

Unknown said...

Hi BalusC,
Undoubtedly a wonderful article on datatables. Thank you so much for it. I used the dynamic datatable part of it. But I have some problems.

1.I need to give a unique id to each row

2.On the click of each row I need to call a javascript function (need to pass the row id)

Unknown said...

Hi Baluc,
I need code in jsf related to below scenario.

Scenario:
when a user select an option from listbox and then user click search button tat option details want to display in a data table within a same page.

Thanks in advance

Anonymous said...

hi BalusC I follow your post specifically the section of "get selected dataTable row" and "save edited dataItem". I have this problem: when I select a row to edit and then I press my button Cancel(action=cancel, I set the navigation to return to the page with the dataTable) and then select other row, it show the rigth data but it modifies the previous I selected ans canceled. Please reply

Parviz said...

I get the main page with all hyper links using (http://localhost:8080/DB_COMPLETE_EXAMPLE/) however when I try any link I get "The requested resource (/DB_COMPLETE_EXAMPLE/session_scope/show_data_contents_in_datatable.jsf) is not available."
I have followed your instruction step by step (or I have tried). Appreciate any help provided.

Dami said...

Nice post! can you share the source code for the ultimate crud?
Thanks

Unknown said...

Hi BalusC!

Thank you for the post firstly.

I'm newbie with JSF and fenced the next problem with dataTable.
I need to add the row with additional information right after each row in the dataTable.

Is it possible to do that?

Unknown said...

Hi Sir,

i have gone through your article and and trying to implement
the functionality of Add new rows to datatable.i have completed this
in Current Project But i am facing an one issue here.

In My Applicationm i am using the same dataTable in 2 scenarios,when i open 2 URLs and
add in one scenario is effecting and adding a row in other also.

and also when i opened in 2 browsers with same url it is coming with rows
added in another browser .

Please help me in solvong this issue.


Regards,
Sateesh.
Email:sateesh90@gmail.com

Unknown said...

Hi Sir,

i have gone through your article and and trying to implement
the functionality of Add new rows to datatable.i have completed this
in Current Project But i am facing an one issue here.

In My Applicationm i am using the same dataTable in 2 scenarios,when i open 2 URLs and
add in one scenario is effecting and adding a row in other also.

and also when i opened in 2 browsers with same url it is coming with rows
added in another browser .

Please help me in solvong this issue.


Regards,
Sateesh.
Email:sateesh90@gmail.com

Piyush said...

@BalusC: Dude !!!! you're the MAN.. this post is much more informative than any JSF book I've glanced.. super awesome dude.. HATS OFF !!! :)

Reema said...

Hi BaluC,

I am trying to create a data table with one of the outputText column getting rendered as 'h:selectOneListbox' on Click of an EDIT button.
Problem - 'h:selectOneListbox' in the datatable column is not able to set the value from a property in the backing bean.
I have also tried to set the default value to 2(for e.g.) but it doesnt get selected from the dropdown list when the page is rendered.

Values in the Dorpdown
ID = 0,Description=""
ID = 1,Description="Completed"
ID = 2,Description="Not Available"

So, as 2 being set as the value, the description="Not Available" must be displayed, but some how the first Description="" is always displayed in the dropdown.

I used the same select dropdown code outside the data table on the same page and it works perfectly.
Please advise.

Thanks

belatrix said...

I would like to know about, how to organize table headers in scrollable and nested datatable as one of the columns in outer table has datable as child component so displaying headers of outer datatable along with inner datatable having body part of table only scrollable creates problems

Anonymous said...

Thank god for your blog, this article helped me quite a lot!
Chapeau!
;-)

Altaf said...

Hi,
I am using JSF 1.2, Richfaces 3.3.1 i need to implement a datatable such that if the value in a column for that row is 1 then it should be displayed and it should be a link.
If the value in that column for another row is 0 then 0 should be displayed but it should NOT be a link.
Basically based on the value in that column it should either be a link or not and also display that value.
Please Help.

Vivek said...

Hi,
I had gone thru your post... Excellent tutorial.. I just wanted to ask Can we associate two data scrollers with one datatable in tomahawk? Actually I was working on a solution to provide the pagination on top and bottom of the page. Is that possible in JSF using tomahawk libs.

Thanks,
Vivek

Vivek said...

Hi,
I had gone thru your post... Excellent tutorial.. I just wanted to ask Can we associate two data scrollers with one datatable in tomahawk? Actually I was working on a solution to provide the pagination on top and bottom of the page. Is that possible in JSF using tomahawk libs.

Thanks,
Vivek

Anonymous said...

Hi please inform to me In treetable we can use Static header for each treenode is possible plz help me

Dipankar said...

ID Generation Issue in JSF datatable nested in data iterator


I am facing a real strange problem. Is this a product issue in JSF?

Followig is my code snippet.






Say my iterator has 4 rows inside it. The generated h:outputText always getting correct id i.e. itr:0:text, itr:1:text, itr:2:text & itr:3:text

However for some strange reason my datatable id inside dataiterator is always generating as itr:0:tbl for all 4 rows of the iterator. This means all 4 my datatables are having same id itr:0:tbl instead of it:0:tbl, itr:1:tbl, itr:2:tbl, itr:3:tbl.

Can anybody throw some light into this? If this is a JSF issue is there any fix availbe for this?

Vimal said...

Hi there,

The best online resource for Java.. Y not start a site\write a book and continue your Good work..

BTW, Thanks a ton!!

ramesh@iitg said...

hi in populate dynamic datatable panelgroup is not rendering if the data comes from database and populates into the dynamic data table from an action but if i load the data into the table in the bean itself and render the panelgroup on acton button the table renders so wats da problem here?? please help me wid dis issue btw ur blog is great!!!

Ankur said...

Hi BalusC

I need a datatable with dynamic scroll height, depending upon the size of the browser window?

Is it possible ?

Unknown said...

hi BalusC
I Want to find Specific Row From data table so how can i do ???

Please Reply urgent Because this requirement is urgent for me ...

Thanks dude....

Rajesh Choudary said...

Hi BalusC,

The List that the datatable is pointing to in the bean if that List has a setter method as well will it create problems.

Thanks,
Rajesh.

Unknown said...

Hello Balusc,

how to generate dynamic drop down list box and text box?

Please help in this.

Thanks,
Srinu.

$$Neerocks said...

Hi BalusC,

Thanks for the article.I created my application based on your following article.
I have few doubts.

My logic works like ..I have a home page .Home pages includes page1,2 & 3 based on user roles.
HomePage > page1,page2,page3

Page1 is a tabbed page(richfaces) which inturn includes page1 & page2 or rather should show the same functionalities.

The problem is when ever i am navigating to page1 or 2 or 3, the backing bean methods for page1,2 & 3 gets executed always.
I tried to put a condition.That seems to be working fine but the problem is during the initial load of the page, home page would not render anything.I need to refresh the page to get the data loaded.

Please provide me a suggestion or some help that would help me to overcome this @neerocks@yahoo.co.in

SANTHOSH said...

How do I show dynamic header content..I mean I need to show it from the bean.

SANTHOSH said...

BalusC, How do I show dynamic headers...I mean I need to show header content from bean..

brokenhearted man said...

THANK YOU is not enough, keep up the awesome work :)

Unknown said...

i tried populating primefaces datatable from inputtext and it is working fine. but my problem is if i refresh the page then last entered values in datatable duplicates and the new row gets new id. please help

Ashutosh.com said...

You are just amazing . I got everything I needed with you post.
Thanks

IndUser said...

How to get the index of hx:datatableEx row on mouse click. Without using commandLink, Checkbox or Radio button.
Thank you.

Unknown said...

how should we add footer to the datatable. And this footer should get update whenever there is any change in the editable column.

Mauricio Nunes said...

Where I can do the download war file? The file does not exist more in box.

BalusC said...

@Maurico: they have apparently changed their URL format. I fixed the download URL.

bhawna said...

HtmlDataTable dt = new HtmlDataTable();
HtmlColumn cd = new HtmlColumn();
HtmlOutputText out= new HtmlOutputText();
out.setValue("header1");
cd.setHeader(out);
dt.getChildren().add(cd);
HtmlOutputText y = new HtmlOutputText();
y.setValue("value1");
cd.getChildren().add(y);
dt.getChildren().add(cd);


I am using the above code but it only displays headers in the form. The content of column is not displayed

BOND said...

Hi, I have the requirement to have 3 columns with a edit command link as fourth column in the data table. The columns can have duplicates values in many rows. The columns are editable input text field but will be disabled on load which has to be enabled on click of edit link. How best to identify which row edit link was clicked so appropriated row input text is enabled. We are using JSF 1.2 hence setpropertyactionlistner cannot be used.

Carlos said...

Hello BalusC,

Could you tell me why JSF does not set null values back to a list (or array)?

I posted a question at Stack Overflow, could you take a look to help me with the solution?

http://stackoverflow.com/questions/27111193/how-to-make-jsf-setting-null-values-into-a-list


Thank you very much!

Unknown said...

how can we export multiple datatables to excel with one export button, typically a message and message details table. Without using primefaces extension ?

Unknown said...

Hello BalusC, I am using p:datatable to display value from list. I put this list in session map in one button click event. now i want to update this list from another jsf page which is open using javascript. after changing values I am getting updated list in controller but it is not updated in datatable. So if any idea then please let me know. Here note that i am not using session bean only use session map to share the object with other jsf page.

hearvishwas said...

Hi Balcus,
Can you assist me to render 2D array of String using datatables.
Thanks in advance

John tallman said...

Hi BalusC,

I am having trouble using omnifaces with data table exporter. the app uses omnifaces viewscope as managed bean. data table is populated via ajax call in the UI. after displaying datatable, clicking the export button gives ViewExpiredException. the export is a commandLink. Please help to resolve this issue.

John tallman said...

Hi BalusC,

I am having trouble using omnifaces with data table exporter. the app uses omnifaces viewscope as managed bean. data table is populated via ajax call in the UI. after displaying datatable, clicking the export button gives ViewExpiredException. the export is a commandLink. Please help to resolve this issue.

BalusC said...

@Unknown: this is a known issue https://github.com/omnifaces/omnifaces/issues/245

Until it's fixed, use h:commandLink instead.

Unknown said...
This comment has been removed by the author.
Unknown said...

/** Business class */


public class RessourcenVerwaltung {

static EntityManagerFactory emf;
static EntityManager em;
static RessourcenVerwaltung instance;

List resourceManagementList = new ArrayList<>();
List reservationList = new ArrayList<>();
List personList = new ArrayList<>();

RessourcenVerwaltung() {
emf = Persistence.createEntityManagerFactory("einzelprojekt");
em = emf.createEntityManager();
}

public void deleteResourceManagement(ResourceManagement
resourceManagement) {
em.getTransaction().begin();
em.remove(resourceManagement);
em.getTransaction().commit();
}


public void addResourceManagement(ResourceManagement resourceManagement) {

em.getTransaction().begin();
em.persist(resourceManagement);
em.getTransaction().commit();
}

public String deleteResourceManagement(ResourceManagement resourceManagement) {
em.getTransaction().begin();
em.remove(resourceManagement);
em.getTransaction().commit();

return null;
}

public String editResourceManagement(ResourceManagement edit) {
em.getTransaction().begin();
em.remove(edit);
em.getTransaction().commit();

return null;
}

// Syso cntrl+ spacebar

public void updateResourceManagement(ResourceManagement resourceManagement) {
em.getTransaction().begin();
em.merge(resourceManagement);
em.getTransaction().commit();
}

public List getResourceManagement() {
TypedQuery query = em.createQuery("select r from ResourceManagement r",
ResourceManagement.class);
return query.getResultList();
}

public List getObjectName(String objectName) {
TypedQuery query = em
.createQuery("select r from ResourceManagement r where name LIKE :name", ResourceManagement.class);
query.setParameter("name", "%" + objectName + "%");
return query.getResultList();
}

public List findAllResourceManagement() {

TypedQuery query = em.createQuery("select r from ResourceManagement r",
ResourceManagement.class);
List resourceManagementList = null;
try {
resourceManagementList = query.getResultList();
} catch (NoResultException e) {
// cry silently
}
return resourceManagementList;
}

public List getResourceManagementList() {
TypedQuery query = em.createQuery("select r from ResourceManagement r",
ResourceManagement.class);
return query.getResultList();
}

public ResourceManagement getResourceManagementByRmId(long rmId) {

TypedQuery query = em.createQuery(
"select r from ResourceManagement r where id=" + Long.toString(rmId), ResourceManagement.class);
return query.getSingleResult();
}

public static RessourcenVerwaltung getInstance() {
if (instance == null) {
instance = new RessourcenVerwaltung();
}
return instance;
}

}

Unknown said...

/** XHTML File */











































































































Unknown said...

/** XHTML File */




xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui">











































































































Unknown said...

Goodday Mr.BalusC,

I am new to JSF and Webapplication.
My aim is to to

1. Save object to DB (Done)
2. Display all objects in DB with h:dataTable JSF
3. Delete and Edit objects.

I ran into your tutorials when searching for answers.
Hope you can help me with some guidelines.
Thanks for yours great tutorials.

Best Regards
John.

giampymc said...

Hi BC, thanks a lot for this post...
I have a problem... I would like to update my columns in back bean. but something doesnt work. I don't know the reason, but in the second call all columns contents has the same values of the first column. Header contents works, the issue is only on column content-value. any ideas? thnx

giampymc said...

Dear BC,
on my code, i had a problem related to column content(the content is the same for all columns, but during the init all works....)
I fixed it!! by assign an Id value to ALL elements in DataTable (column, panelGrid, etc)
Ex. htmlPanelGroup.setId("grid1");

Kind regards.

Vinay Sharma said...

Hi Balus C,

I am facing issue with html command link . If i used hyperlink in my table i got error 405 if i click any where on my page.

I have dynmically created links and solution is working fine with old version of JSF . Now we have migrated to JSF 2.1 . Html command button is working fine if i remove hyperlink from my page.

Code Snippet :-
* Create a command link.

*
* @param id
* @param label
* @param action
* @param attributes
* @param immediate
* @return HtmlCommandLink
*/
public static HtmlCommandLink newHtmlCommandLink(String id, String label, String action,
Map attributes, boolean immediate) {
HtmlCommandLink commandLink = new com.icesoft.faces.component.ext.HtmlCommandLink();
commandLink.setId("lnk_" + id);
MethodBinding methodBinding = FacesHelper.createMethodBinding(action, null);
commandLink.setAction(methodBinding);
commandLink.setImmediate(immediate);
setValue(commandLink, label);
assignAttributes(commandLink, attributes);
return commandLink;
}

error Request URL:http://127.0.0.1:9080/application/secure/[object HTMLInputElement]-- wrong url created
Request method:POST
Remote address:127.0.0.1:9080
Status code:
405

Moni said...

Hi BalusC,

I have a datatable where i need to add a row with text field and selectOnemenu tag so i can add multiple rows but when selectonemenu has multiple values and when i select one value from
each in multiple rows, it is overriding the last value when clicking on submit button. can you please suggest solution for this ?

Raju said...

BalusC, Thanks for your article and it's very useful.. i have one question in case if we have input text field for each row with same id, so in this case how can we get the input values in the bean class. please help me on this issue.

«Oldest ‹Older   201 – 317 of 317   Newer› Newest»