Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Sunday, May 3, 2020

OmniFaces 3.6 adds manifest.json generator, o:scriptParam, and o:pathParam

OmniFaces 3.6 has been released!

Next to a bunch of utility methods, this version adds a WebAppManifestResourceHandler which autogenerates the manifest.json based on properties of a CDI bean, the <o:scriptParam> which allows you to set evaluated JavaScript results in a managed bean, and a <o:pathParam> which can be used in UIOutcomeTarget components (such as <h:link>) to populate desired path parameters for pages covered by MultiViews.

You can find the complete list of additions, changes and fixes at What's new in OmniFaces 3.6? list in showcase. One of most important changes is that the of:formatDateXxx() functions now also support java.time.Temporal instances.

Installation

Non-Maven users: download OmniFaces 3.6.1 JAR and drop it in /WEB-INF/lib the usual way, replacing the older version if any.

Maven users: use <version>3.6.1</version>.

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>3.6.1</version>
</dependency>

WebAppManifestResourceHandler

This resource handler takes care of automatically generating a manifest.json file, which is one of the minimum requirements for a Progressive Web Application (PWA). All you need to do is to extend from the org.omnifaces.resourcehandler.WebAppManifest class, implement and override the desired getter methods, put a CDI scope annotation on it such as @ApplicationScoped, and finally reference it in your Facelet template exactly as below:

<link rel="manifest" href="#{resource['omnifaces:manifest.json']}" />

And you're done! See also the showcase.

o:scriptParam

This new little brother of <hashParam> is capable of running some JavaScript code on page load, collect its results and populate the managed bean with them. The @PostScriptParam annotation can be used to invoke a method after the script params have been populated.

For example,

<f:metadata>
    <o:scriptParam script="new Date().timeZoneOffset()" value="#{bean.clientTimeZoneOffset}" />
</f:metadata>
private Integer clientTimeZoneOffset; // +getter +setter

@PostScriptParam
public void initScriptParams() {
    System.out.println("The client time zone offset is: " + clientTimeZoneOffset);
}

See also the live demo.

o:pathParam

In case you're using MultiViews, i.e. when you have for example a /users.xhtml page which can be opened as /users/42/john-doe, then you can use the new <o:pathParam> to safely URI-encode the path parameters into any link to /users.xhtml.

For example,

<ui:repeat value="#{bean.users}" var="user">
    <h:link outcome="/users" value="#{user.name}">
        <o:pathParam value="#{user.id}">
        <o:pathParam value="#{user.slug}">
    </h:link>
</ui:repeat>

See also the showcase.

How about OmniFaces 2.x and 1.1x?

The 2.x got the same bugfixes as 3.6 and has been released as 2.7.5. This version is for JSF 2.2 users with CDI. In case you've already migrated to JSF 2.3, use 3.x instead.

The 1.1x is basically already since 2.5 in maintenance mode. I.e. only critical bugfix versions will be released. It's currently still at 1.14.1 (May 2017), featuring the same features as OmniFaces 2.4, but without any JSF 2.2 and CDI things and therefore compatible with CDI-less JSF 2.0/2.1.

Monday, June 2, 2014

OmniFaces 1.8.3 released!

OmniFaces 1.8.3 has finally been released!

Also this release had some unscheduled delay for various reasons. Great programmers are also just humans with a "life" next to all the development work. I personally had after all a lot more time needed to acclimatize myself to the Netherlands after having lived in CuraƧao for almost 6 years (still don't really feel home here outside the working hours, still want to go back once the kids grow out the house). Arjan had among others also some unforeseen issues with his new home.

As usual, in the What's new page of the showcase site you can find an overview of all what's been added/changed/fixed for 1.8. The three most useful additions are the <o:deferredScript>, <o:massAttribute> and @Eager.

Installation

Non-Maven users: download OmniFaces 1.8.3 JAR and drop it in /WEB-INF/lib the usual way, replacing the older version if any.

Maven users: use <version>1.8.3</version>.

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>1.8.3</version>
</dependency>

Defer loading and parsing of JavaScript files

If you've ever analyzed the performance of your website using a tool like Google PageSpeed, then you'll probably recognize the recommendation to defer loading and parsing of JavaScript files. Basically, the recommendation is to load JavaScript files only when the browser is finished with rendering of the page. This is to be achieved by dynamically creating a <script> element via document.createElement() during window.onload. Please note that this is not the same as just moving the scripts to the bottom of the page using <h:outputScript target="body">! That would speed up downloading of other resources, but still block the rendering of the HTML in most browsers (read: everything expect IE).

OmniFaces now comes with a <o:deferredScript> component for this very purpose which works just like <h:outputScript> with a library and name attribute.

<h:head>
    ...
    <o:deferredScript library="libraryname" name="resourcename.js" />
</h:head>

You can also use it on for example PrimeFaces scripts, but some additional work needs to be done. For detail, refer this Stack Overflow Question and Answer: Defer loading and parsing of PrimeFaces JavaScript files. At a production site, this approach has proven to decrease the average time until "DOM content loaded" from ~3s to ~1s on a modern client machine.

Set a common attribute on multiple components

The new <o:massAttribute> taghandler allows you to set a common attribute on multiple components. So, instead of for example:

<h:inputText ... disabled="#{someBean.disabled}" />
<h:inputText ... disabled="#{someBean.disabled}" />
<h:inputText ... disabled="#{someBean.disabled}" />
<h:inputText ... disabled="#{someBean.disabled}" />
<h:inputText ... disabled="#{someBean.disabled}" />

You can just do:

<o:massAttribute name="disabled" value="#{someBean.disabled}">
    <h:inputText ... />
    <h:inputText ... />
    <h:inputText ... />
    <h:inputText ... />
    <h:inputText ... />
</o:massAttribute>

The advantage speaks for itself.

Eagerly instantiate a CDI managed bean

When using the standard JSF managed bean facility via @ManagedBean which is since JSF 2.2 semi-official deprecated (not documented as such, but the JSF team is clearly pushing toward it given the total absence of new features around JSF managed bean facility, instead they are all in CDI managed bean facility), it was possible to declare an application scoped JSF managed bean to be eagerly instantiated during application's startup like so:

import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;

@ManagedBean(eager=true)
@ApplicationScoped
public class Bean {
    // ...
}

However, this isn't possible with standard CDI, not even with the one as available in Java EE 7. So OmniFaces has added the @Eager and @Startup annotations for the very purpose. The @Startup is just a stereotype for @Eager @ApplicationScoped.

So, both beans below are equivalent:

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import org.omnifaces.cdi.Eager;

@Named
@Eager
@ApplicationScoped
public class Bean {
    // ...
}
import javax.inject.Named;
import org.omnifaces.cdi.Startup;

@Named
@Startup
public class Bean {
    // ...
}

An additional bonus of OmniFaces @Eager is that it not only works on application scoped CDI managed beans, but also on request and session scoped CDI managed beans and on beans annotated with OmniFaces @ViewScoped (thus not the JSF 2.2 one yet, that will come in the upcoming OmniFaces 2.0 which will target JSF 2.2, OmniFaces 1.x is namely JSF 2.0 targeted).

Having @Eager on a request scoped bean may look somewhat strange, but this makes an interesting use case possible: eagerly and asynchronously fetch some data from a DB in the very beginning of the request, long before the FacesServlet is invoked, it runs even before the servlet filters are hit (it's initiated via a ServletRequestListener). Depending on the server hardware used, the available server resources, all code running between the invocation of the first servlet filter and entering the JSF render response, this may give you a time space of 10ms ~ 500ms (or perhaps more if you've some inefficient code in the pipeline ;) ) to fetch some data from DB in a different thread parallel with the HTTP request and thus a speed improvement equivalent to the time the DB needs to fetch the data. Below is an example of how such an approach can look like:

The asynchronous service (this silly example fetches the entire table; just do whatever DB or any other relatively long-lasting service task you want to do as long as the method is annotated with @Asynchronous and you return an AsyncResult as Future; the container will all by itself worry about managing the threads):

package com.example;

import java.util.List;
import java.util.concurrent.Future;
import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless
public class MyEntityService {

    @PersistenceContext
    private EntityManager em;

    @Asynchronous
    public Future<List<MyEntity>> asyncList() {
        List<MyEntity> entities = em
            .createQuery("SELECT e FROM MyEntity e", MyEntity.class)
            .getResultList();
        return new AsyncResult<>(entities);
    }

}

The @Eager request scoped bean (note the requestURI attribute, this must exactly match the context-relative request URI without any path fragments and query strings, this example assumes a /test.xhtml page (with a FacesServlet mapping of *.xhtml); wildcards like * are not supported yet, this may come in the future if there's demand)

package com.example;

import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.omnifaces.cdi.Eager;

@Named
@Eager(requestURI="/test.xhtml")
@RequestScoped
public class MyEagerRequestBean {

    private Future<List<MyEntity>> entities;

    @Inject
    private MyEntityService service;

    @PostConstruct
    public void init() {
        entities = service.asyncList();
    }

    public List<MyEntity> getEntities() {
        try {
            return entities.get();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new FacesException(e);
        } catch (ExecutionException e) {
            throw new FacesException(e);
        }
    }

}

This way, when you request /test.xhtml with something like this:

<h:dataTable value="#{myEagerRequestBean.entities}" var="entity">
    <h:column>#{entity.property}</h:column>
</h:dataTable>

... then the above bean will be constructed and initialized far before the FacesServlet is invoked. Note that this thus also means that the FacesContext is not available inside the @PostConstruct! From that point on, both JSF and JPA will do their jobs simultaneously in separate threads until JSF calls the getter for the first time. The JSF thread (the HTTP request thread) will then block until JPA has returned the result, or perhaps it's already returned at that moment and then JSF can just advance immediately without waiting for JPA.

An overview of all additions/changes/bugfixes in OmniFaces 1.8

Taken over from the What's new? page on showcase:

Added in OmniFaces 1.8

  • WebXml#getFormErrorPage() to get web.xml configured location of the FORM authentication error page
  • <o:deferredScript> which is capable of deferring JavaScript resources to window.onload
  • Faces#addResponseCookie() got 2 new overloaded methods whereby domain and path defaults to current request domain and current path
  • Components#isRendered() which also checks the rendered attribute of all parents of the given component
  • <o:massAttribute> which sets the given attribute on all nested components
  • FacesMessageExceptionHandler which sets any caught exception as a global FATAL faces message
  • <o:cache> has new disabled attribute to temporarily disable the cache and pass-through children directly
  • @Eager annotation to eagerly instantiate request-, view-, session- and application scoped beans
  • <o:viewParam> skips converter for null model values so that query string doesn't get polluted with an empty string
  • Small amount of utility methods and classes, e.g. method to check CDI annotations recursively in stereotypes, shortcut method to obtain VDL, etc

Changed in OmniFaces 1.8

  • CombinedResourceHandler now also recognizes and combines <o:deferredScript>
  • UnmappedResourceHandler now also recognizes PrimeFaces dynamic resources using StreamedContent

Fixed in OmniFaces 1.8

  • Assume RuntimeException in BeanManager#init() as CDI not available (fixes deployment error on WAS 8.5 without CDI enabled)
  • Use "-" (hyphen) instead of null as default option value to workaround noSelectionOption fail with GenericEnumConverter
  • <o:param> shouldn't silently convert the value to String (fixes e.g. java.util.Date formatting fail in <o:outputFormat>)
  • Fixed javax.enterprise.inject.AmbiguousResolutionException in subclassed @FacesConverter and @FacesValidator
  • <o:messages> failed to find the for component when it's not in the same parent
  • <o:conditionalComment> shouldn't XML-escape the if value, enabling usage of & character
  • UnmappedResourceHandler broke state saving when partial state saving is turned off
  • CombinedResourceHandler didn't properly deal with MyFaces-managed resources

Maven download stats

Here are the Maven download stats:

  • January 2014: 3537
  • February 2014: 3580
  • March 2014: 3892
  • April 2014: 3572
  • May 2014: 3971

Below is the version pie of May 2014:

Last but not least

For the case you missed it: OmniFaces repo, wiki and issue tracking (basically: everything) has moved from Google Code to GitHub, along with a "brand new" homepage in GitHub style at omnifaces.org. The downloads will from now just point directly to Maven via links at the homepage.

Monday, May 25, 2009

Java/JSP/JSF and JavaScript

Introduction

Today I investigated using Google Analytics the search keywords used to find this blog, so that I can get some blogging inspiration based on the "missing hits" (i.e. the actual search incorrectly showed my blog between the results). Just for fun I entered "javascript" in the Analytics keyword-search tool to see what happens. To my surprise I see that fairly a lot of Google search keywords in the following context were used:

  • access jsf managed bean in javascript
  • javascript pass variable to jsp
  • check jsf facesmessages in javascript
  • call java method in javascript
  • set javascript variable in jsp
  • communicate jsf and javascript

There's apparently still a lot of confusion and unawareness in the (young) web developer world with regard to Java/JSP/JSF and JS. Well, let's write a blog about it to clear it all out.

Back to top

Server side and client side

You probably have ever heard of "server side" and "client side" in terms of web development. You have server side specific technologies and programming languages and also client side specific technologies and programming languages (well, not specifically programming, it's in terms of web development more scripting and markup, but OK, let's make it easy understandable).

When one develops websites, one would often use physically the same machine (PC, laptop, whatever) to develop and test websites on. I.e., both the webserver and webbrowser runs at physically the same machine. This would never, I repeat, never occur in real production world. The server machine and the client machine are always physically different machines, connected with each other by some kind of a network (usually the Internet).

Java/JSP/JSF are server side specific technologies. It runs at the server machine and produces HTML/CSS/JS output based on a HTTP request of the client machine. This output will be sent over network from the server side to the client side as part of the HTTP response. Open up such a JSP/JSF page in your favourite webbrowser and choose the "View Source" option (or something similar). What do you see? Right, it's all plain vanilla HTML/CSS/JS. If the code at the server side has done its task the right way, you should not see any line of Java/JSP/JSF code in the generated output.

When the server application (in this case, the webserver) has sent the output, it has finished its task of HTTP request/response cycle and is now waiting for the next HTTP request from the client side. When the client application (in this case, the webbrowser) has received the HTML/CSS/JS output, displayed the HTML, applied the CSS and executed any "onload" JS, it has finished its task of showing the result of some client interaction (entering URL in address bar, clicking a link, submitting a form, etcetera) and is now waiting for the next client interaction to fire a new HTTP request for.

JS is a client side specific technology. It runs entirely at the client machine and it only has direct access to the whole HTML DOM tree (the stuff which is accessible through the document object). It would only get executed directly when the client application (the webbrowser) encounters inline JS code or onload events during loading of the page for display. It can also get executed later when the client application receives specific events which have a JS function attached, such as onclick, onkeypress, onmouseover, etcetera. All of those events can be specified as HTML element attributes.

You should now realize that the only way to let Java/JSP/JSF at the server side pass variables to or execute something at the client side is simply by generating the HTTP response for the client side that way so that the JS action will be taken accordingly. You should also realize that the only way to let JS at the client side pass variables to or execute something at the server side is by simply firing a new HTTP request to the server side that way so that the Java/JSP/JSF action will be taken accordingly.

Back to top

Pass variables from server side to client side

As you might have understood now, this can only happen by writing inline JS code or onload events accordingly. Let's check first how the generated HTML output should look like then:

<!doctype html>
<html>
    <head>
        <title>Test</title>
        <script type="text/javascript">
            // Do something inline with variable from server.
            var variableFromServer = 'variableFromServer';
            doSomethingInline(variableFromServer);
            function doSomethingInline(variable) {
                alert('doSomethingInline: ' + variable);
            }

            // Do something onload with variable from server.
            function doSomethingOnload(variable) {
                alert('doSomethingOnload: ' + variable);
            }
        </script>
    </head>
    <body onload="doSomethingOnload('variableFromServer');">
        <h1>Test</h1>
    </body>
</html>

The above is just a basic example, you could also use for example an onclick event instead. OK, in the place of variableFromServer we thus want to set a variable from the server side. You can just do it by writing the server side code accordingly that the desired generated HTML output is exact the same as above. Let's suppose that the variable have to be retrieved as a bean property. Here's an example with JSP:

<!doctype html>
<html>
    <head>
        <title>Test</title>
        <script type="text/javascript">
            // Do something inline with variable from server.
            var variableFromServer = '${someBean.someProperty}';
            doSomethingInline(variableFromServer);
            function doSomethingInline(variable) {
                alert('doSomethingInline: ' + variable);
            }

            // Do something onload with variable from server.
            function doSomethingOnload(variable) {
                alert('doSomethingOnload: ' + variable);
            }
        </script>
    </head>
    <body onload="doSomethingOnload('${someBean.someProperty}');">
        <h1>Test</h1>
    </body>
</html>

Fairly simple, is it? Take note that you still need those singlequotes around the variable; not for Java/JSP, but for JavaScript! Only pure number or boolean variables can be left unquoted.

In JSF the principle is not that much different. You could use EL the same way, but if it has to be retrieved as a managed bean property and you're using JSF on JSP, you can just use h:outputText instead to output it. Here's an example:

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

<!doctype html>
<f:view>
    <html>
        <head>
            <title>Test</title>
            <script type="text/javascript">
                // Do something inline with variable from server.
                var variableFromServer = '<h:outputText value="#{someBean.someProperty}" />';
                doSomethingInline(variableFromServer);
                function doSomethingInline(variable) {
                    alert('doSomethingInline: ' + variable);
                }

                // Do something onload with variable from server.
                function doSomethingOnload(variable) {
                    alert('doSomethingOnload: ' + variable);
                }
            </script>
        </head>
        <body onload="doSomethingOnload('<h:outputText value="#{someBean.someProperty}" />');">
            <h1>Test</h1>
        </body>
    </html>
<f:view>

Run the page and check the generated HTML output. Do you see it? That's the whole point! This way you can let JS "access" the server side variables. When you're using JSF on Facelets, then you can just leave the h:outputText away and use #{someBean.someProperty} instead since Facelets supports unified EL in template text.

In the code examples the JS code will be executed two times. The first time when the line with the inline JS function call is interpreted by the client application and the second time when the page is loaded completely (the body onload attribute). The biggest difference is that the second function will only be executed when the entire HTML DOM tree is built up, this is important if the function require HTML DOM elements which are to be obtained by the document reference in JS.

Back to top

Pass variables from client side to server side

As you probably already know, you can do that with plain HTML by just clicking a link with query parameters, or submitting a form with (hidden) input parameters. Links sends HTTP GET requests only, while forms are capable of sending HTTP POST requests. In JS, on the other hand, there are in general three ways to accomplish this:

  1. The first way is to simulate invocation of an existing link/button/form. Here are three examples:
    <a id="linkId" href="http://www.google.com/search?q=balusc">Link</a>
    
    ...
    
    <script type="text/javascript">
        document.getElementById('linkId').click();
    </script>
    
    <form action="http://www.google.com/search">
        <input type="text" id="inputId" name="q">
        <input type="submit" id="buttonId" value="Button">
    </form>
    
    ...
    
    <script type="text/javascript">
        document.getElementById('inputId').value = 'balusc';
        document.getElementById('buttonId').click();
    </script>
    
    <form id="formId" action="http://www.google.com/search">
        <input type="text" id="inputId" name="q">
    </form>
    
    ...
    
    <script type="text/javascript">
        document.getElementById('inputId').value = 'balusc';
        document.getElementById('formId').submit();
    </script>
    
    In case of JSF, keep in mind that you must use the element ID's of the JSF generated HTML elements (the JSF Client ID's) in JS. Those are namely not necessarily the same as JSF component ID's! So, view the generated HTML output when writing JS code specific for JSF output. For example the following JSF code..
    <h:form id="formId">
        <h:inputText id="inputId" value="#{bean.property}" />
        <h:commandButton id="buttonId" value="Button" action="#{bean.action}" />
    </h:form>
    
    ..would produce roughly the following HTML output..
    <form id="formId" action="current request URL">
        <input type="text" id="formId:inputId" name="formId:inputId" />
        <input type="submit" id="formId:buttonId" name="formId:buttonId" value="Button" />
    </form>
    
    ..for which you should write JS like the following:
    <script type="text/javascript">
        document.getElementById('formId:inputId').value = 'foo';
        document.getElementById('formId:buttonId').click();
        // Or: document.getElementById('formId').submit();
    </script>
    
  2. The second way is to use window.location to fire a plain GET request. For example:
    <script type="text/javascript">
        var search = 'balusc';
        window.location = 'http://www.google.com/search?q=' + search;
    </script>
    
  3. The third way is to use XMLHttpRequest object to fire an asynchronous request and process the results. This technique is the base idea of "Ajax". Here's a Firefox compatible example:
    <script type="text/javascript">
        function getUrl(search) {
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function() {
                if (xhr.readyState == 4) {
                    var responseJson = eval('(' + xhr.responseText + ')');
                    var url = responseJson.responseData.results[0].unescapedUrl;
                    var link = document.getElementById('linkId');
                    link.href = link.firstChild.nodeValue = url;
                    link.onclick = null;
                }
            }
            var google = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q='
            xhr.open('GET', google + search, true);
            xhr.send(null);
        }
    </script>
    
    ...
    
    <p>My homepage is located at: <a id="linkId" href="#" onclick="getUrl('balusc')">click me!</a></p>
    
    Funny, is it? You didn't see a "flash of content" (reload/refresh) because it all happens fully in the background. That's the nice thing of Ajax. For compatibility with certain webbrowsers which uses proprietary API's (cough) you'll need to write some supplementary code. Also see the Ajax tutorial at w3schools.

    In case of JSP, it's better to start with a widely used Ajaxical API than reinventing the wheel by writing it yourself, which would at end only lead to trouble in terms of maintainability and reusability. I highly recommend to take a look for the great jQuery API. Its replacement of the getUrl() would then look something like:
    function getUrl(search) {
        var google = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=';
        $.getJSON(google + search, function(responseJson) {
            var url = responseJson.responseData.results[0].unescapedUrl;
            $('#linkId').attr('href', url).text(url).attr('onclick', null);
        });
    }
    
    On the server side, instead of the Google example you could of course create a Servlet which has doGet() implemented and returns for example a JSON string so that JS could process it further. JSON is a shorthand for JavaScript Object Notation. It is roughly a blueprint of a JavaScript object in String format. You can even specify and access object properties. In the eye of a Java developer, a JSON has much Javabean characteristics. If you're new to JSON (or even JavaScript or HTML DOM), here are more tutorials: JavaScript, HTML DOM and JSON. For converting Java objects to a JSON string which you just write to response, I can recommend the GSON API.
    
        /**
         * Write given Java object as JSON to the output of the given response.
         * @param response The response to write the given Java object as JSON to its output.
         * @param object Any Java Object to be written as JSON to the output of the given response. 
         * @throws IOException If something fails at IO level.
         */
        public void writeJson(HttpServletResponse response, Object object) throws IOException {
            String json = new Gson().toJson(object);
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
            response.getWriter().write(json);
        }
    
    

    In case of JSF, just take a look for Ajaxical component libraries such as JBoss RichFaces. It provides "supplementary" Ajax support in form of the Ajax4jsf (a4j) components, also see this document.

Hopefully it's now all clear what the whole point of "server side" and "client side" is and what their capabilities are.

Happy coding!

Back to top

Copyright - No text of this article may be taken over without explicit authorisation. Only the code is free of copyright. You can copy, change and distribute the code freely. Just mentioning this site should be fair.

(C) May 2009, BalusC

Friday, December 14, 2007

Set focus and highlight in JSF

WARNING - OUTDATED CONTENT!

This article is targeted on JSF 1.2. Whilst this can be used in JSF 2.0 as well, a better JSF 2.0 implementation is provided by OmniFaces with its <o:highlight> component. See also showcase example here.

The power of a PhaseListener

This article shows how to use a PhaseListener to set focus to the first input element which has a FacesMessage (which can be caused by a validation or conversion error or any other custom reason) and highlight all elements which has a FacesMessage. It is relatively simple, it costs effectively only a few lines inside the beforePhase of the RENDER_RESPONSE, two small Javascript functions for the focus and highlight and a single CSS class for the highlight.

Here is a sample form. Note the Javascript which should be placed at the very end of the HTML body, at least after the input element which should be focused and highlighted. The stuff is tested in a Java EE 5.0 environment with Tomcat 6.0 with Servlet 2.5, JSP 2.1 and JSF 1.2_07 (currently called Mojarra).

<h:form id="form">
    <h:panelGrid columns="3">
        <h:outputLabel for="input1" value="Enter input 1" />
        <h:inputText id="input1" value="#{myBean.input1}" required="true" />
        <h:message for="input1" style="color: red;" />

        <h:outputLabel for="input2" value="Enter input 2" />
        <h:inputText id="input2" value="#{myBean.input2}" required="true" />
        <h:message for="input2" style="color: red;" />

        <h:outputLabel for="input3" value="Enter input 3" />
        <h:inputText id="input3" value="#{myBean.input3}" required="true" />
        <h:message for="input3" style="color: red;" />

        <h:panelGroup />
        <h:commandButton value="Submit" action="#{myBean.doSomething}" />
        <h:panelGroup />
    </h:panelGrid>
</h:form>

<script>
    setHighlight('${highlight}');
    setFocus('${focus}');
</script>

This is how the Javascript functions setFocus() and setHighlight() should look like:

/**
 * Set focus on the element of the given id.
 * @param id The id of the element to set focus on.
 */
function setFocus(id) {
    var element = document.getElementById(id);
    if (element && element.focus) {
        element.focus();
    }
}

/**
 * Set highlight on the elements of the given ids. It basically sets the classname of the elements
 * to 'highlight'. This require at least a CSS style class '.highlight'.
 * @param ids The ids of the elements to be highlighted, comma separated.
 */
function setHighlight(ids) {
    var idsArray = ids.split(",");
    for (var i = 0; i < idsArray.length; i++) {
        var element = document.getElementById(idsArray[i]);
        if (element) {
            element.className = 'highlight';
        }
    }
}

And now the CSS style class for the highlight:

.highlight {
    background-color: #fcc;
}

And finally the PhaseListener which sets the focus to the first input element which has a FacesMessage and highlights all input elements which has a FacesMessage:

/*
 * net/balusc/webapp/SetFocusListener.java
 * 
 * Copyright (C) 2007 BalusC
 * 
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with this library.
 * If not, see <http://www.gnu.org/licenses/>.
 */

package net.balusc.webapp;

import java.util.Iterator;

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

/**
 * This phase listener checks if there is a client ID with message and will set the client ID as 
 * ${focus} in the request map. It will also gather all client IDs with message and will set it as
 * ${highlight} in the request map.
 * <p>
 * This phase listener should be configured in the faces-config.xml as follows:
 * <pre>
 * &lt;lifecycle&gt;
 *     &lt;phase-listener&gt;net.balusc.webapp.SetFocusListener&lt;/phase-listener&gt;
 * &lt;/lifecycle&gt;
 * </pre>
 * 
 * @author BalusC
 * @link http://balusc.blogspot.com/2007/12/set-focus-in-jsf.html
 */
public class SetFocusListener implements PhaseListener {

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

    /**
     * @see javax.faces.event.PhaseListener#getPhaseId()
     */
    public PhaseId getPhaseId() {

        // Listen on render response phase.
        return PhaseId.RENDER_RESPONSE;
    }

    /**
     * @see javax.faces.event.PhaseListener#beforePhase(javax.faces.event.PhaseEvent)
     */
    public void beforePhase(PhaseEvent event) {

        // Init.
        FacesContext facesContext = event.getFacesContext();
        String focus = null;
        StringBuilder highlight = new StringBuilder();

        // Iterate over all client ID's with messages.
        Iterator<String> clientIdsWithMessages = facesContext.getClientIdsWithMessages();
        while (clientIdsWithMessages.hasNext()) {
            String clientIdWithMessages = clientIdsWithMessages.next();
            if (focus == null) {
                focus = clientIdWithMessages;
            }
            highlight.append(clientIdWithMessages);
            if (clientIdsWithMessages.hasNext()) {
                highlight.append(",");
            }
        }

        // Set ${focus} and ${highlight} in JSP.
        facesContext.getExternalContext().getRequestMap().put("focus", focus);
        facesContext.getExternalContext().getRequestMap().put("highlight", highlight.toString());
    }

    /**
     * @see javax.faces.event.PhaseListener#afterPhase(javax.faces.event.PhaseEvent)
     */
    public void afterPhase(PhaseEvent event) {
        // Do nothing.
    }

}

Define the SetFocusListener as follows in the faces-config.xml:

    <lifecycle>
        <phase-listener>net.balusc.webapp.SetFocusListener</phase-listener>
    </lifecycle>

That's all, folks!

Back to top

Copyright - GNU Lesser General Public License

(C) December 2007, BalusC

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