JSF 2 fu: Best practices for composite components

Implement extensible custom components

In this installment of JSF 2 fu, you'll learn five best practices for implementing Java™Server Faces composite components. By following these guidelines, you'll make it easy for page authors to extend your custom components.

David Geary, President, Clarity Training, Inc.

David GearyAuthor, speaker, and consultant David Geary is the president of Clarity Training, Inc., where he teaches developers to implement Web applications using JSF and Google Web Toolkit (GWT). He was on the JSTL 1.0 and JSF 1.0/2.0 Expert Groups, co-authored Sun's Web Developer Certification Exam, and has contributed to open source projects, including Apache Struts and Apache Shale. David's Graphic Java Swing was one of the best-selling Java books of all time, and Core JSF (co-written with Cay Horstman), is the best-selling JSF book. David also speaks regularly at conferences and user groups. He has been a regular on the NFJS tour since 2003, is a three-time Java University instructor, and a three-time JavaOne Rock Star.



11 January 2011

Also available in Chinese Japanese Portuguese

About this series

The JSF 2 fu series, a follow-on to David Geary's three-article introduction of the same name, will help you develop and hone your JSF 2 framework skills like a kung fu master. The current series dives deeper into the framework and its surrounding ecosystem. And it takes a peek outside the box by showing how some Java EE technologies, such as Contexts and Dependency Injection, integrate with JSF.

JSF is a component-based framework, which means that it provides the infrastructure you need to implement your own components. JSF 2 provides a simple way to implement custom components with composites.

I've shown you the implementation of several composite components in previous articles (see "Templating and composite components," "Ajax components," and "After-the-fact Ajax composite components"). In this article, I'll wrap up that topic — and the JSF 2 fu series — by presenting five best practices for implementing composite components with JSF 2:

  1. Wrap your components in a DIV.
  2. Incorporate JavaScript and Ajax.
  3. Use JavaScript closures to support multiple components in a page.
  4. Let page authors customize your components.
  5. Internationalize your components.

To illustrate these best practices, I'll discuss how they apply to the implementation of a simple composite component.

The editable input composite component

This article's example component is an editable input composite component. The application shown in Figure 1 uses two editable inputs, one for first name and one for last name:

Figure 1. Editable text components
Three screenshots showing the editing sequence for the first name, from top to bottom

From top to bottom, the three screenshots in Figure 1 show the editing sequence for the first name:

  • The top screenshot shows the application's initial appearance, with edit... buttons sitting to the right of First name: and Last name: labels.
  • The middle screenshot shows how the application looks immediately after the user clicks the edit... button next to First name: and enters Roger into a text-input area. A done button appears to the right of the text-input area.
  • The bottom screenshot shows how the application looks after the user has clicked the done button. Now First name: Roger displays, with an edit... button to the right of that.

Next, I'll discuss how to use the editable input component and then show you how it's implemented. After that, I'll discuss each of the five best practices in terms of the component's implementation.

Using the component

You use the editable input component as you use any JSF composite component: declare the appropriate namespace, and use the tag that JSF generates for the composite component. Listing 1 illustrates those two steps with the markup for the page shown in Figure 1:

Listing 1. Using <util:inputEditable>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:util="http://java.sun.com/jsf/composite/util">
   <h:head>
      <title>Implementing custom components</title>
   </h:head>
   <h:body>
      <h:form>   
         <h:panelGrid columns="2">
            First name: 
            <util:inputEditable id="firstName" 
                             value="#{user.firstName}"/>
               
            Last name:
            <util:inputEditable id="lastName" 
                             value="#{user.lastName}"/>
        </h:panelGrid>
      </h:form>
   </h:body>  
</html>

For completeness, Listing 2 shows the implementation of the user bean referenced in Listing 1:

Listing 2. The User bean
package com.corejsf;

import java.io.Serializable;
import javax.inject.Named; 
import javax.enterprise.context.SessionScoped; 

@Named("user") 
@SessionScoped
public class UserBean implements Serializable {
   private String firstName;
   private String lastName;

   public String getFirstName() { return firstName; }   
   public void setFirstName(String newValue) { firstName = newValue; }

   public String getLastName() { return lastName; }
   public void setLastName(String newValue) { lastName = newValue; }   
}

Now that you've seen how to use the editable input component, I'll show you how it's implemented.

The component's implementation

The editable input component is implemented in the inputEditable.js, inputEditable.properties, and inputEditable.xhtml files in the resources/util directory, as shown in the filesystem hierarchy in Figure 2:

Figure 2. The component's files
Screenshot of the folder and file hierarchy for the inputEditable component

Listing 3 shows inputEditable.xhtml:

Listing 3. The inputEditable component's markup (inputEditable.xhtml)
<!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:f="http://java.sun.com/jsf/core"
    xmlns:composite="http://java.sun.com/jsf/composite">
    
    <composite:interface>
       <composite:attribute name="text"/>
       <composite:editableValueHolder name="text" targets="editableText" />
       <composite:actionSource name="editButton" targets="editButton" />
       <composite:actionSource name="doneButton" targets="doneButton" />
       <composite:clientBehavior name="edit" event="action" targets="editButton"/>
       <composite:clientBehavior name="done" event="action" targets="doneButton"/>
       <composite:facet name="textMessage"/>
    </composite:interface>

    <composite:implementation>
      <h:outputScript library="javascript" name="prototype.js" target="head"/>
      <h:outputScript library="javascript" name="scriptaculous.js" target="head"/>
      <h:outputScript library="javascript" name="effects.js" target="head"/>   
      <h:outputScript library="util" name="inputEditable.js" target="head"/>
          
      <div id="#{cc.clientId}">
         <h:outputText id="text" value="#{cc.attrs.text}"/>          
         <h:commandButton id="editButton" type="button" 
            value="#{cc.resourceBundleMap.editButtonText}"
            onclick="this.startEditing()"/>

         <h:inputText id="editableText" value="#{cc.attrs.text}" style="display: none"/>
        
         <h:commandButton id="doneButton" 
            value="#{cc.resourceBundleMap.doneButtonText}" style="display: none">
          
             <f:ajax render="text textMessage" execute="editableText" 
               onevent="ajaxExecuting"/>
            
         </h:commandButton>
       
         <h:panelGroup id="textMessage">
            <composite:renderFacet name="textMessage"/>
         </h:panelGroup>
      </div>
      
      <script> com.clarity.init('#{cc.clientId}'); </script>
    
    </composite:implementation>    
</html>

The markup creates four components, only two of which — the text and the edit button — are initially visible. When the user clicks the edit button, the application calls the com.clarity.startEditing() JavaScript function, which is implemented in Listing 4:

Listing 4. The inputEditable component's JavaScript (inputEditable.js)
package com.corejsf;
   var com = {};

if (!com.clarity) {
   com.clarity = {
      init: function (ccid) {
         var mydiv = document.getElementById(ccid);
         mydiv.editButton = $(mydiv.id + ':editButton');
         mydiv.text = $(mydiv.id + ':text');
         mydiv.editableText = $(mydiv.id + ':editableText');
         mydiv.doneButton = $(mydiv.id + ':doneButton');
         mydiv.doneButton.offsetLeft = mydiv.editButton.offsetLeft;
           
         mydiv.editButton.startEditing = function() {
           mydiv.text.fade( { duration: 0.25 } );
           mydiv.editButton.fade( { duration: 0.25 } );
      
           window.setTimeout( function() {
              mydiv.editableText.appear( { duration: 0.25 } );
              mydiv.doneButton.appear( { duration: 0.25 } );
      
               window.setTimeout( function() { 
                  mydiv.editableText.focus(); 
               }, 300); 
            }, 300); 
         };
      },
   
      toggleDisplay: function(element) {
         element.style.display = element.style.display == "none" ? "" : "none"; 
      },
           
      ajaxExecuting: function(data) {
         var mydiv = $(data.source.parentNode);
      
         if (data.status == 'complete') {
            toggleDisplay(mydiv.editableText);
            toggleDisplay(mydiv.doneButton);
            toggleDisplay(mydiv.text);
            toggleDisplay(mydiv.editButton);
         }
      }
   }
}

The startEditing() function uses the fade() and appear() methods from the Scriptaculous framework (see Resources). It also uses a couple of timers to ensure that the fades and appearances happen in the correct order. Notice that the startEditing() function also ultimately gives focus to the text input.

Listing 5 shows inputEditable.properties:

Listing 5. The inputEditable component's properties file (inputEditable.properties)
editButtonText=edit...
doneButtonText=done

Next, I'll discuss editable input's implementation from the perspective of the five best practices.


Wrap components in a DIV

When JSF creates a composite component, it creates what the framework refers to as a naming container, which contains all of the components inside the composite. The naming container, however, does not generate markup; instead, JSF generates markup for each of the components inside the composite. As a result, the page markup can't reference the component as a whole by its component ID, because by default there's no component with that ID.

You can give page authors the ability to reference the composite component by wrapping the component in a DIV when you implement it. Suppose, for example, that you want the page markup to refer to an editable input component as part of an Ajax call. In the following markup, I add an Ajax button that processes the first-name input. Clicking on the button makes an Ajax call to the server, where the first-name input is processed; when the Ajax call returns, JSF renders the input:

<h:form>   
   <h:panelGrid columns="2">
      First name: 
      <util:inputEditable id="firstName" 
                       value="#{user.firstName}"/>
               
      Last name:
      <util:inputEditable id="lastName" 
                       value="#{user.lastName}"/>

      <h:commandButton value="Update first name">
         <f:ajax execute="firstName" render="firstName">
      </h:commandButton>
  </h:panelGrid>
</h:form>

The Ajax button in the preceding markup works because in Listing 3 I wrapped the component in a DIV:

<div id="#{cc.clientId}">

   ...

</div>

The identifier used for the DIV is the client identifier of the composite itself. Therefore, the page author can refer to the composite component as a whole, as does the Ajax button I just discussed.


Incorporate JavaScript and Ajax

One thing you can't see in the static screenshots in this article is the fade animation that the editable input component performs when the user clicks the edit... button. That fade is ultimately done by the Scriptaculous framework. In Listing 3, I use the <h:outputScript> tag to output Scriptaculous' required JavaScript, and in Listing 4 I use the framework's fade() and appear() methods to achieve the desired animation:

mydiv.editButton.startEditing = function() {
  mydiv.text.fade( { duration: 0.25 } );
  mydiv.editButton.fade( { duration: 0.25 } );
      
  window.setTimeout( function() {
     mydiv.editableText.appear( { duration: 0.25 } );
     mydiv.doneButton.appear( { duration: 0.25 } );
      
      window.setTimeout( function() { 
         mydiv.editableText.focus(); 
      }, 300); 
   }, 300); 
};

The nested timers in the preceding JavaScript make sure that everything in the animation happens on cue. For example, I postpone giving focus to the input text until I'm sure the input has appeared onscreen; otherwise, if I call focus() before the input appears, the call won't stick.

It's simple to use third-party JavaScript frameworks, such as Scriptaculous or JQuery, with JSF. You output the appropriate JavaScript in the page, and then use the framework in your JavaScript code.

The editable input component also uses Ajax to make a call to the server when the user clicks the done button:

<h:commandButton id="doneButton" 
   value="#{cc.resourceBundleMap.doneButtonText}" style="display: none">
          
   <f:ajax render="text textMessage" execute="editableText" 
      onevent="ajaxExecuting"/>
            
</h:commandButton>

In the preceding markup, I use JSF's <f:ajax> tag to make an Ajax call when the user clicks the done button. That Ajax call executes the text input on the server and updates the text and the text message when the Ajax call returns.


Use JavaScript closures

When you implement composite components, you must account for multiple components in a page. When all instances of a component share the same JavaScript, you must be careful to manipulate only the component that the user is currently interacting with.

You can support multiple components in a page in several ways. One way, discussed by Oracle engineer Jim Driscoll in a blog entry about a similar editable input component, is to maintain a namespace of component IDs (see Resources). Another way is to use JavaScript closures:

   com.clarity = {
      init: function (ccid) {
         var mydiv = document.getElementById(ccid);
         mydiv.editButton = $(mydiv.id + ':editButton');
         mydiv.text = $(mydiv.id + ':text');
         mydiv.editableText = $(mydiv.id + ':editableText');
         mydiv.doneButton = $(mydiv.id + ':doneButton');
         mydiv.doneButton.offsetLeft = mydiv.editButton.offsetLeft;
           
         mydiv.editButton.startEditing = function() {
           mydiv.text.fade( { duration: 0.25 } );
           mydiv.editButton.fade( { duration: 0.25 } );
      
           window.setTimeout( function() {
              mydiv.editableText.appear( { duration: 0.25 } );
              mydiv.doneButton.appear( { duration: 0.25 } );
      
               window.setTimeout( function() { 
                  mydiv.editableText.focus(); 
               }, 300); 
            }, 300); 
         };
      },

This is the JavaScript for the editable input component that's shown in its entirety in Listing 4. The init() function is called for every editable input component, as you can see at the bottom of Listing 3. Given the client identifier of the component's enclosing DIV, I get a reference to that specific DIV. And because JavaScript is a dynamic language that lets you add properties and methods to objects at run time, I add references to all the elements that I need later in my callback functions to the DIV itself.

I also add a startEditing() method to the component's edit button. When the user clicks edit..., I invoke that method:

<h:commandButton id="editButton" type="button" 
   value="#{cc.resourceBundleMap.editButtonText}"
   onclick="this.startEditing()"/>

When the startEditing() function is invoked, the mydiv variable retains the original value that it had when the init() method was called. That's the cool thing about JavaScript closures (and to some extent, Java inner classes). It doesn't matter how much time elapses between init() and startEditing(), and it doesn't matter if init() has been called a bunch of times in between — when startEditing() is invoked, its value of mydiv is its very own copy that it had when the init() method was called for that particular component.

Because JavaScript closures retain values of a surrounding function's variables, you can be sure that each startEditing() function accesses the appropriate DIV for its component.


Let page authors customize

With typically a mere line or two of XML in your component definition, you can let page authors customize your components. The three main ways to customize composite components are:

  • Adding validators, converters, and listeners
  • Facets
  • Ajax

Validators, converters, and listeners

You can let page authors attach validators, converters, and listeners to components inside your composite components, as long as you expose those inner components. For example, Figure 3 shows validation added to an editable input component:

Figure 3. Validating the first-name field
Validating the first-name field

Figure 3 displays an error message stating that the field requires at least 10 characters. The page author has added a validator to the first-name editable input component's text, like this:

<util:inputEditable id="firstname" text="#{user.firstName}">
   <f:validateLength minimum="10" for="text"/>
</util:inputEditable>

Notice the for attribute of the <f:validateLength> tag. That tells JSF that the validator is for the text inside the editable input component. JSF knows about that text, because I exposed it in the component's implementation:

<composite:interface>
   ...
   <composite:editableValueHolder name="text" targets="editableText" />
   ...
</composite:interface>

<composite:implementation>
   ...
   <h:inputText id="editableText" value="#{cc.attrs.text}" style="display: none"/>
   ...
</composite:implementation>

Facets

In Figure 3, the error message is displayed at the bottom of the page. That's because I'm using the Development project stage, and JSF automatically adds validation errors at the bottom. You can't tell which editable input the error is associated with, so it would be better to put the error message next to the offending component, as shown in Figure 4:

Figure 4. Using facets
Using facets

The page author can do this by adding a facet to the component:

<util:inputEditable id="firstname" text="#{user.firstName}">
   <f:validateLength minimum="10" for="text"/>
   <f:facet name="textMessage">
      <h:message for="editableText" style="color: red"/>
   </f:facet>
</util:inputEditable>

That facet is supported by the component:

<composite:interface>
   <composite:facet name="textMessage"/>
</composite:interface>
   
<div id="#{cc.clientId}">
   <h:panelGroup id="textMessage">
      <composite:renderFacet name="textMessage"/>
   </h:panelGroup>
   ...
</div>

Ajax

As I discussed in "After-the-fact Ajax composite components," you can use the <composite:clientBehavior> tag to let page authors add Ajax capabilities to your composite components. Figure 5 shows a dialog box that monitors Ajax calls that take place when the user clicks the edit... button:

Figure 5. Monitoring Ajax requests
Monitoring Ajax requests

The page author just adds an <f:ajax> tag to the component and specifies an Ajax function whose onevent attribute is a JavaScript function (the function that displays the dialog box) to call as the Ajax call is progressing:

<util:inputEditable id="firstname" text="#{user.firstName}">
   <f:validateLength minimum="10" for="text"/>
   <f:facet name="textMessage">
      <h:message for="editableText" style="color: red"/>
   </f:facet>

   <f:ajax event="edit" onevent="monitorAjax"/>
</util:inputEditable>

The page author can add an <f:ajax> tag to the component because I've exposed that client behavior in the component's implementation:

<composite:interface>
   <composite:clientBehavior name="edit" event="action" targets="editButton"/>
</composite:interface>

Internationalize

The editable input component displays some text (edit... and done) on its two buttons. For the component to be used with multiple locales, that text must be internationalized and localized.

To localize a component's text, simply add a properties file in the same directory as the component, and then access keys in the properties file through this expression: #{cc.resourceBundleMap.KEY}, where KEY is the key in the properties file. As you can see from Listing 3 and Listing 5, that's how I localize the text for the editable input component's buttons.


Conclusion

JSF 1 made it difficult to implement components, so most JSF developers opted not to. With JSF 2, custom components are no longer the exclusive realm of custom component framework developers. In this article, I've shown you some best practices for implementing composite components. With a little work, you can make your components easily extensible for page authors.

Resources

Learn

Get products and technologies

  • JSF: Download JSF 2.0.

Discuss

  • Get involved in the My developerWorks community. Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Java technology on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology, Web development
ArticleID=606762
ArticleTitle=JSF 2 fu: Best practices for composite components
publish-date=01112011