Saturday, February 9, 2013

Advanced RichFaces Extended Data Tables

 
With Mardi Gras and Carnival around the corner, this is crêpe season!

This post is the first of a set of recipes that describe useful tips and reusable code and patterns related to Java, JSF 2.0 and RichFaces 4.0.  In this article I am describing practical integration code when using advanced Richfaces Extended Data Tables.

The RichFaces showcase describes well how to start creating your own extendedDataTable. However, here are some additional personal ingredients that can be very handy when preparing an appealing stack of JSF based web pages.





1) Setting your Table

The first time your try to put together a RichFaces extendedDataTable, you might encounter the following behavior: your table does not look like as you expected it: columns have a default size (the length of the label) and the whole size is the size of your whole window, so you end up with a large white space where they are no columns:


 One reason could be that no column or table size has been specified:


 <rich:extendedDataTable id="table" value="#{countries.countryItems} var="country" >
    <f:facet name="header">
        <h:outputText value="Countries" />
    </f:facet>
    <rich:column>
        <f:facet name="header">
            <h:outputText value="Name" />
        </f:facet>
        <h:outputText value="#{country.name}" />
    </rich:column>
    <rich:column>
        <f:facet name="header">
            <h:outputText value="Capital" />
        </f:facet>
        <h:outputText value="#{country.capital}" />
    </rich:column>
    <rich:column>
        <f:facet name="header">
            <h:outputText value="Language(s)" />
        </f:facet>
        <h:outputText value="#{country.languages}" />
    </rich:column>
</rich:extendedDataTable>


One way to quickly fix this is to assign specific sizes to your columns and headers (in my case these are absolute values in pixels and have adjusted the values to take into account the height of a row and the presence of a vertical scroller).  I have also added single selection mode so I can click on a row to retrieve more information about a certain item:

<rich:extendedDataTable id="table" value="#{countries.countryItems}"
    var="country" selection="#{countries.selection}" 
    style="height:190px; width:503px;"
    selectionMode="single">
    <a4j:ajax execute="@form" event="selectionchange" listener="#{countries.selectionListener}"/>
        <f:facet name="header">
            <h:outputText value="Countries" />
        </f:facet>
        <rich:column width="100px">
            <f:facet name="header">
                <h:outputText value="Name" />
            </f:facet>
            <h:outputText value="#{country.name}" />
        </rich:column>
        <rich:column width="100px">
            <f:facet name="header">
                 <h:outputText value="Capital" />
            </f:facet>
            <h:outputText value="#{country.capital}" />
        </rich:column>
        <rich:column width="300px">
            <f:facet name="header">
                <h:outputText value="Language(s)" />
            </f:facet>
            <h:outputText value="#{country.languages}" />
        </rich:column>
</rich:extendedDataTable>
















2) Interaction improvements

Let say now that you want to add a button to load or refresh the information in the table and that this takes some time because you are connected to a remote service. You may want to show to the end-user an indicator that some processing or data retrieval is happening (for this example, I added a waiting function in the iem list bean to simulate a few seconds wait).

I propose to use an animated GIF and take advantage of the RichFaces a4j:status (an indicator of an Ajax request. It has two states: start and stop. The start state indicates that an Ajax request is in progress. When an Ajax response is returned, the component switches to the stop state).

In this case, I refer to a4j:status every time I click on the refresh button to download the countries statistics. I have also added an animated GIF file called processing.gif in my webapp/img folder and top aligned both the button and the ajax indicator into a JSF panel grid:


<h:panelGroup>
    <h:panelGrid columns="2" columnClasses="alignTop, alignTop">
        <a4j:commandButton value="Refresh" status="refreshTable"
     oncomplete="#{countries.refresh()}" />
        <a4j:status name="someProcessing">
            <f:facet name="start">
         <h:graphicImage value="/img/processing.gif" />
     </f:facet>
        </a4j:status>
    </h:panelGrid>
</h:panelGroup>

As a result, the processing indicator appears next to the button when it is clicked and disappear after the table has been refreshed.


















In fact, you can use the same indicator when selecting a row to display detailed information about an item if the retrieval of the additional information takes too long for the user (e.g. more than a couple of seconds):


<a4j:ajax execute="@form" event="selectionchange" status="someProcessing" listener="#{countries.selectionListener}" />


3) Adding some coloring and flavor

If you don't have a predefine look and feel (e.g. a reusable template or UI toolkit), you can easily use the themes and skins that come with RichFaces.  A skin can be quickly added to the web.xml file as follow:


<context-param>
    <param-name>org.richfaces.skin</param-name>
    <param-value>wine</param-value>
</context-param>

Some of my favorites predefined skins are deepMarine and wine:

 
















4) The missing ingredients

Even though the RichFaces Extended Data Tables have a lot of features such as filtering, sorting, scrolling, frozen columns, here are always missing ingredients that you would like to use to offer a better experience to the end user. One that you will not find in the ExtendedDataTable is the wrapping of text in a column or a robust column horizontal scrolling. Hopefully this feature will be added in future release.


5) Always give a tip if you can

Offering tips is generally a good practice. It makes feel everybody happy: the person who provides it knowing that he/she has done a good job, and the end-user who enjoys it and saves time using them.

In this example, I have added a column to to indicate which countries have a GDP above one Trillion US Dollars. When the use hovers the mouse above the checkbox or the name of the country, a tooltip appears and shows the GDP number for the selected country.

















Tooltips can be easily added as follow:

<rich:column styleClass="#{msg.status}" width="35px">
    <f:facet name="header">
        <h:outputText value="GDP > $1T" />
    </f:facet>
    <rich:tooltip mode="client" target="largeGDPCountry">
        <h:outputText value="#{country.name} - 2011 GDP: #{country.gdp} > $1T" />
    </rich:tooltip>
    <h:graphicImage id="largeGDPCountry" value="/img/checkmark.png" rendered="#{country.largeGdp}" alt="GDP" />
</rich:column>
<rich:column width="100px">
    <f:facet name="header">
        <h:outputText value="Name" />
    </f:facet>
    <h:outputText id="countryName" value="#{country.name}" />
    <rich:tooltip mode="client" target="countryName">
        <h:outputText value="#{country.name} - 2011 GDP: #{country.gdp} $M" />
    </rich:tooltip>
</rich:column>


Ingredients and open source code related to this recipe can be found at YummyCode on the JSF Plate project.

No comments: