Creating Composite Components in JSF2

Written on March 26, 2012 by Allan Lykke Christensen

I’m in the early stages of upgrading Converge from JSF 1.2 to JSF 2. I’ve been holding back for too long but finally decided that now is the time to get it done. Initially I was waiting for component libraries to be released for JSF 2. In the current version of Converge we are using RichFaces 3, which we’ve been very happy with (We tried out other component libraries but non of them worked out very well for us). However, with JSF2 it is now possible to easily create your own components using the Composite Components setup. For people who are sharp with client-side technologies such as Ajax, you may actually end up creating all your owner components rather than using the available third party libraries such as RichFaces, IceFaces and PrimeFaces. It is not that the third party libraries are bad, but with custom components you’ve got 100% control of exactly how your components get rendered. To give a small demonstration of composite components I put together a small simple example. The purpose of the example is to demonstrate the ease of creating components.

Open Data

Anyone body who knows me, knows that I love Open Data. I use a lot of datasets from Google Fusion Tables, Infochimps, and the numerous Open Data Initiatives (such as http://data.worldbank.org and http://data.un.org).  So, in my simple example, I’ll create a component that generates an HTML table based on an existing, public Google Fusion table.

Step 1: Defining the tag

The first step in creating a composite component is to define its interface (i.e. the attributes you allow developers to specify when using your tag). For our component we need the developer to specific which Google Fusion table to show data from. We’ll call this attribute tableId. We also need the developer to specific which fields of the table to display. We’ll call this attribute fields.

So our tag would look like this:

<my:fusionTable tableId="..." fields="..." />

Step 2: Creating the composite tag file

Composite component tag fields reside in the directory /resources/ of your web-application. By adding another directory in this directory you create a namespace for your components. In our case we use the namespace my so you’d have a directory structure like this: /resources/my/. Create a file in this directory called fusionTable.xhtml with the following content:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:cc="http://java.sun.com/jsf/composite">
 
    <!-- INTERFACE -->
    <cc:interface>
    </cc:interface>
 
    <!-- IMPLEMENTATION -->
    <cc:implementation>
    </cc:implementation>
</html>

The section <cc:interface /> is for defining what the component accept of attributes and action sources. The <cc:implementation /> section is for implementing the actual output of the component.

Since we already defined the attributes of the component we can declare it in the <cc:interface /> section like so:

<!-- INTERFACE -->
<cc:interface>
    <cc:attribute name="tableId" required="true" type="java.lang.String"  />
    <cc:attribute name="fields" required="true" type="java.lang.String" />
</cc:interface>

The section above declares the tableId and fields attribute. It also specifies that the two attribute are plain strings and that they are required.

Step 3: Implement the tag

Now that the interface is in place we need to specify the output of the tag. If you use the tag as it is now, it will require you to specify the declared attributes, but the output will be a blank screen. To get the content of a Fusion Table we use the public SQL API provided by Google. The API supports the JSON format which is convenient to work with in jQuery and JavaScript. To use jQuery in our component we must first download a copy of the jQuery library and place it in the /resources/js directory. Next we must indicate that the component needs this JavaScript to operate. We do that using the outputScript tag in <cc:implementation /> section:

<!-- IMPLEMENTATION -->
<cc:implementation>
    <h:outputScript library="js" name="jquery.js" target="head" />
</cc:implementation>

Note: If the jQuery script has already been included by another component or page, it will not be included again.

Next we need to call the Fusion Tables API using the getJSON jQuery function and process the output returned. We do all of this in a JavaScript contained in the implementation of the component:

    <!-- IMPLEMENTATION -->
    <cc:implementation>
        <h:outputScript library="js" name="jquery.js" target="head" />
        <script type="text/javascript">
            // URL encode the fields
            var fields = "";
            // Unique ID of the Fusion Table
            var tableId = "";
 
            // Construct the FusionTable service URL
            // Note: jsonCallback ensures that the call is jsonp
            var service = 'https://www.google.com/fusiontables/api/query?sql=SELECT%20' 
                + fields + '%20FROM%20#' + tableId + '&amp;jsonCallback=?';
 
            // Call the service and generate HTML based on the response
            $.getJSON(service, 
            function(data) {
 
                // Array used to collect HTML as the response is processed
                var items = [];
 
                // Generate the table header (columns)
                items.push('<thead>');
                items.push('<tr>');
                for (var key in data.table.cols) {
                    items.push('<th>' + data.table.cols[key] + '</th>');
                }
                items.push('</tr>');
                items.push('</thead>');
 
                // Generate the table data rows
                items.push('<tbody>');
                // Read data rows into an array
                for (var key in data.table.rows) {
 
                    items.push('<tr>');
                    for (var col in data.table.rows[key]) {
                        items.push('<td>' + data.table.rows[key][col] + '</td>');
                    }
                    items.push('</tr>');
                }
                items.push('</tbody>');
 
                // Output generated table
                $('<table />', { 
                    'class': '#{cc.attrs.styleClass}', 
                    'id': '#{cc.attrs.clientId}',  
                    html: items.join('')
                }).appendTo('body');
            });
        </script>

The URL for retrieving a Fusion Table is:

https://www.google.com/fusiontables/api/query?sql=SELECT fields FROM table&jsonCallback=?

The URL has two query parameters sql containing the SQL statement to execute and jsonCallback=? which allows the call to be turned into a JSONP call to avoid cross-domain query restrictions in the browser. Note that you must URL encode the query parameters to avoid problems submitting the query.

Once the query has been posted, the response is processed and the HTML of the component is generated by iterating over the content.

The last thing we need to do is to take the values entered by the developer and put them in the JavaScript. This is done by replacing the empty values in the beginning of the implementation section:

var fields = escape("#{cc.attrs.fields}");
var tableId= "#{cc.attrs.tableId}";

This will take the value specified in the fields attribute, URL encoded it, and store it in the JavaScript variable fields. The tableId does not need to be URL encoded and is copied across to JavaScript verbatim.

Step 4: Using the tag

All that is remaining now is to use the tag. Before we can use the tag we must first identify a Google Fusion Table and its fields. For my example I found a public Fusion Table containing the Corruption Index for 2011 by Transparency International. The field names are easy to see, e.g. 2011 rank (containing the ranking of a given country on the corruption index), Country / Territory (containing the name of the country), ISO (containing the ISO code of the country), etc. To find the TableId click File and select About. The TableID is the string specified as the Encrypted ID (in our case 1QWBy7LcULz6pN1SuHxm2HtSSd9jMg9IYbPDZRiM).

Using our component we want to show the ranking and name of the country. The component usage would therefore look something like this:

 
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:my="http://java.sun.com/jsf/composite/my">
    <h:head>
        <title>FusionTable Component Test</title>
    </h:head>
    <h:body>  
        <h1>Corruption Index</h1>
        <my:fusionTable id="tblCorruption" 
                        tableId="1QWBy7LcULz6pN1SuHxm2HtSSd9jMg9IYbPDZRiM" 
                        fields="'2011 rank','Country / Territory'" />
    </h:body>
</html>

Notice that field names with spaces must be included in single quotes.

That’s all. As you can see, it is really easy to create your own components. The speed of creation comes down to your skills with the client-side technology (e.g. jQuery). If you are terrible with HTML, CSS, JavaScript and jQuery, I’d suggest you stick to the available third party component libraries. If you are fluent in the client technologies and want full control over what you are doing you should go ahead and experiment with custom composite components.

If you enjoyed this post Subscribe to our feed