You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by Apache Wiki <wi...@apache.org> on 2006/04/26 20:32:45 UTC

[Myfaces Wiki] Update of "Working with auto sortable tables" by CatalinKormos

Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Myfaces Wiki" for change notification.

The following page has been changed by CatalinKormos:
http://wiki.apache.org/myfaces/Working_with_auto_sortable_tables

New page:
It's a common requirement to display a data set into a table, and in most cases there is also a need to let the user sort that data set by columns. The most simplest and probably very often encountered case is when the entire data set is returned at once for display, and the sorting should be performed by comparing the values displayed in each cell of a given column (i.e. properties of the row object). You'll need to handle the sorting in the backing bean, and use <t:commandSortHeader> for each sortable column of each data table of yours, like the following:

{{{
<t:dataTable var="car"
             value="#{list.cars}"
             sortColumn="#{list.sort}"
             sortAscending="#{list.ascending}"
             preserveDataModel="true"
             preserveSort="true">
        <h:column>
            <f:facet name="header">
                <t:commandSortHeader columnName="type" arrow="true">     
                    <h:outputText value="Type"/>                    
                </t:commandSortHeader>
            </f:facet>
            <h:outputText value="#{car.type}" />            
        </h:column>
        <h:column>
            <f:facet name="header">
                <t:commandSortHeader columnName="color" arrow="true">                    
                    <h:outputText value="Color" />
                </t:commandSortHeader>
            </f:facet>
            <h:outputText value="#{car.color}" />                    
        </h:column>
</t:dataTable>
}}}

And the code snippet to perform sort (where a SimpleCar instance represents a row object):

{{{
public String sort(String column)
{
   Comparator comparator = new Comparator()
   {
       public int compare(Object o1, Object o2)
       {
           SimpleCar c1 = (SimpleCar)o1;
           SimpleCar c2 = (SimpleCar)o2;
           if (column == null)
           {
               return 0;
           }
           if (column.equals("type"))
           {
               return ascending ? c1.getType().compareTo(c2.getType()) : c2.getType().compareTo(c1.getType());
           }
           else if (column.equals("color"))
           {
               return ascending ? c1.getColor().compareTo(c2.getColor()) : c2.getColor().compareTo(c1.getColor());
           }
           else return 0;
      }
   };
   Collections.sort(_cars, comparator);
}
}}}

The MyFaces extended data table provides a more painless solution for this kind of cases. You only need to specify sortable="true" on the data table, and you'll get the same effect without any other custom code. Please note the use of <t:column> component instead of <h:column> which allows to specify the default sorted column, and the sort(String column) method is not needed any more in your backing bean:

{{{
<t:dataTable var="car"
             value="#{list.cars}"
             sortColumn="#{list.sortColumn}"
             sortAscending="#{list.ascending}"
             preserveDataModel="true"
             preserveSort="true"
             sortable="true">
        <t:column defaultSorted="true">
            <f:facet name="header">
                <h:outputText value="Type"/>                    
            </f:facet>
            <h:outputText value="#{car.type}" />            
        </t:column>
        <t:column>
            <f:facet name="header">
                <h:outputText value="Color" />
            </f:facet>
            <h:outputText value="#{car.color}" />                    
        </t:column>
</t:dataTable>
}}}

=== How does automatic sorting work ===

If sortable="true" the data table will do the following:
 1. wrapp the current model with a sortable one and make it the current model (this wrapper model is provided by MyFaces)
 2. iterate over each column and wrapp the current content of the header facet with a command sort header component
 3. while iterating over each column, get the first output component child of the column and find the property of the row object that is used to display the cell content, from it's value attribute
 
This would get the table into the same state as if you had specified the sort header for each column's header facet. Then the sorting is handled in the sortable model. 

=== How are the sort properties are determined ===

As previously mentioned, when sortable="true" the data table will iterate over the columns and get the sort property from the value attribute of the first output component found in the column's children list. This is done by parsing the expression string of the value binding. For example, this a column:
{{{
<t:column>
   <f:facet name="header">
       <h:outputText value="Color" />
   </f:facet>
   <h:outputText value="#{car.color}" />                    
</t:column>
}}}

The first output component would then be:
{{{
<h:outputText value="#{car.color}" /> 
}}}

And the value binding's expression string:
{{{
#{car.color}
}}}

What we are interested at this point is to get the value "color" as the property of the row object from this expression string. The string is parsed and the first thing it searches for, is the appearence of "car." where "car" is the value of the var attribute of the data table.

Of course, this is the simplest case, but more complex case are parsed correctly also, like, nested properties and collections. For example, the following will work also:

{{{
#{car.color.type} or #{car.color[3]}
}}}

Another thing to note, it that if the value binding is composed from several expressions using some kind of operators, only the first appearence of a property of the row object will be taken. For example:

{{{
#{car.price + car.taxes} => the property taken is "price" and this will be used when sorting
}}}

=== Current limitations ===

 1. for sorting, only properties available on a row object can be used, and these must be comparable, i.e, implement the Comparable interface, or else they will be compared as strings
 2. using sortable="true" makes all the columns sortable, it's not possible yet to have only some of the columns sortable.

If you need to customize the sort property that is used to sort the values for a column, you can use the propertyName attribute of the command sort header component like in the following example, cause the sort headers are added only if not already present:

{{{
<t:dataTable var="car"
             value="#{list.cars}"
             sortColumn="#{list.sortColumn}"
             sortAscending="#{list.ascending}"
             preserveDataModel="true"
             preserveSort="true"
             sortable="true">
        <t:column defaultSorted="true">
            <f:facet name="header">
                <t:commandSortHeader columnName="type" arrow="true" propertyName="id">  
                    <h:outputText value="Type"/>   
                </t:commandSortHeader>
            </f:facet>
            <h:outputText value="#{car.type}" />            
        </t:column>
        <t:column>
            <f:facet name="header">
                <h:outputText value="Color" />
            </f:facet>
            <h:outputText value="#{car.color}" />                    
        </t:column>
</t:dataTable>
}}}