Project Documentation
Foundation

Description

The MyFacesDataTable extends the standard JSF DataTable by two important features:

  • Possiblity to save the state of the DataModel.
  • Support for clickable sort headers (see SortHeader component).

Extended data_table that adds some additional features to the standard data_table action: see attribute descriptions for preserveDataModel, sortColumn, sortAscending and preserveSort.

Screen Shot

datatable

API

component-family javax.faces.Data
renderer-type org.apache.myfaces.Table
component-class org.apache.myfaces.component.html.ext.HtmlDataTable
renderer-class org.apache.myfaces.renderkit.html.ext.HtmlTableRenderer
tag-class org.apache.myfaces.taglib.html.ext.HtmlDataTableTag

Usage

<t:dataTable [ all standard dataTable attributes allowed ]
                [ preserveDataModel="{true|false}" ]
                [ preserveRowStates="{true|false}" ]
                [ forceIdIndexFormula="value-binding" ]
                [ sortColumn="value-binding" ]
                [ sortAscending="value-binding" ]
                [ preserveSort="{true|false}" ] 
                [ renderedIfEmpty="{true|false}" ]
                [ rowIndexVar="variable name" ]
                [ rowCountVar="variable name" ]
                [ previousRowDataVar="variable name" ]
                [ rowId="value-binding" ]
                [ newspaperColumns="value-binding" ] 
                [ newspaperOrientation="value-binding" ] 
                [ rowStyleClass="css styleclass" ]
                [ rowStyle="inline css style" ]
                [ rowOnClick="javascript" ]
                [ rowOnDblClick="javascript" ]
                [ rowOnMouseDown="javascript" ]
                [ rowOnMouseUp="javascript" ]
                [ rowOnMouseOver="javascript" ]
                [ rowOnMouseMove="javascript" ]
                [ rowOnMouseOut="javascript" ]
                [ rowOnKeyPress="javascript" ]
                [ rowOnKeyDown="javascript" ]
                [ rowOnKeyUp="javascript" ]>
    standard dataTable body (<h:column> tags 
    and optional "header", "footer", and "spacer" facets)
    <t:column>
    <t:columns>
    <t:command_sortheader/> inside column header or footer
<t:dataTable>
            

Syntax

<t:dataTable>

all standard dataTable attributes allowed
preserveDataModel="{true|false}" - Indicates whether the state for each row should not be discarded before the datatable is rendered again. Setting this to true might be hepful if an input component inside the datatable has no valuebinding and the value entered in there should be displayed again, or if the input component model is not updated. For example, during validation failure, immediate flag short-circuiting, or subForms. This will only work reliable if the datamodel of the datatable did not change either by sorting, removing or inserting rows. You can use HtmlDataTable.clearRowStates() or HtmlDataTable.deleteRowStateForRow(int deletedIndex) to programically modify the saved row states. Default: false
preserveRowStates="{true|false}"
forceIdIndexFormula="value-binding"
sortColumn="value-binding"
sortAscending="value-binding"
preserveSort="{true|false}"
renderedIfEmpty="{true|false}" - Indicates whether this table should be rendered if the underlying DataModel is empty.
rowIndexVar="variable name" - A parameter name, under which the current rowIndex is set in request scope similar to the var parameter.
rowCountVar="variable name" - A parameter name, under which the rowCount is set in request scope similar to the var parameter.
previousRowDataVar="variable name" - A parameter name, under which the previous RowData Object is set in request scope similar to the rowIndexVar and rowCountVar parameters.
rowId="value-binding" - The id to use for <tr> elements that are generated by the table.
newspaperColumns="value-binding" - The number of layout columns to wrap the table through.
newspaperOrientation="value-binding" - The orientation of the columns in the table.
rowStyleClass="css style class" - the style class to use for <tr> elements that are generated by the table. Can be a value-binding to assign row-data specific style classes
rowStyle="inline css style" - inline style to use for <tr> elements that are generated by the table. Can be a value-binding to assign row-data specific style

Each event handler must evaluate to a javascript code which is executed on the client side. The event handler are used for the <tr> tag of the row.
You can use value-bindings for row specific event handler.
rowOnClick="javascript"
rowOnDblClick="javascript"
rowOnMouseDown="javascript"
rowOnMouseUp="javascript"
rowOnMouseOver="javascript"
rowOnMouseMove="javascript"
rowOnMouseOut="javascript"
rowOnKeyPress="javascript"
rowOnKeyDown="javascript"
rowOnKeyUp="javascript"


Instructions

Saving the state of the DataModel - the preserveDataModel attribute
When this attribute is "true", the data behind the current DataModel is saved after the render response phase and restored in the restore component tree phase.

Why and when use this feature?
Whenever you use a DataModel backed by a database connection you could run into problems, when the data in the database has changed since the last request. All Lifecycle phases prior to the render response phase iterate the DataModel and assume that the DataModel is unchanged since the last request. At least the row count must not have changed, because all children of UIData that are bound to the DataModel rely on it. But even if you can assure that row count never changes, a change in the data can have unintentional sideeffects.
Using the preserveDataModel feature prevents such problems. The DataModel (to be more exact: the currently visible part of the DataModel given by the first and the rows attribute) is freezed right after rendering and you can be sure that all lifecycle actions during the next request happen on exactly the same data.

What data types are supported?
To be able to save the state of the DataModel the row objects must be serializable. All standard DataModel types are supported, except ResultSet, which will follow in one of the next releases,

Is updating the model supported?
Yes. Just make your bean property that is bound to the DataTable component writable, i.e. give it a setter method.

To minimize the effort for saving the state of the DataModel only the visible rows are saved and restored. During the update model phase the setter will be called with an Array or List, that contains only these restored rows.

Wrapping table layout -- the newspaperColumns attribute and spacer facet
The newspaperColumns attribute allows a long, narrow table to be wrapped so that it becomes a short, wide table. This allows more information to be shown on a single screen. This is commonly used to present checkboxes for a long list of items. Use the "spacer" facet to specify a component displayed between layout columns. The newspaperOrientation attribute specifies if the columns will be layed out in a vertical or horizontal manner,
Example:

<t:dataTable newspaperColumns="3" value="#{addressBB.states}" newspaperOrientation="horizontal" var="state">
    <f:facet name="spacer">
		  <f:verbatim>&amp;#160;</f:verbatim>
    </f:facet>
    <h:column>
		  <h:outputText value="#{state.abbr}"/>
    </h:column>
    <h:column>
        <h:outputText value="#{state.name}"/>
    </h:column>
</t:dataTable>

Working with a changing data - the forceIdIndexFormula attribute
The default table assumes that your backing data collection is stable between 2 requests. This assumption can be false in several cases: concurrent accesses, unstable backing data collection order, ... If you have components within that table that update your data, you will get unintended behaviors.
This attribute is meant to fix that problem.
The table's components and the backing data objects are linked by the table's components' ids. By default the changing part of those ids is the row number. So if the backing data positions in the list is changed between the 2 requests (like an element inserted in the head of the list), you will update the wrong element in the backing data's collection.
To fix this, you can set the forceIdIndexFormula to an EL that will be unique and stable for each row. That way, even if your backing data list changes between 2 requests, you always update the intended element in the list.

Example : <t:dataTable value="#{mailDAO.userInbox}" var="email" forceIdIndexFormula="#{email.primaryKey}" ...> ...

Warning : make sure that the value-binding evaluates to a unique value for each row.