Skip to main content

Combine JSF with Dojo widgets to create a better user experience

Dan Wang (wangdsh@cn.ibm.com), Software Engineer, IBM, Software Group
Photo of Dan Want
Dan Wang has worked in the IBM China Development Lab since 2006, and has participated in several J2EE projects. He is interested in new Web technology including Web 2.0 and mashup technologies.
Wei Huang (hwcdl@cn.ibm.com), Software Engineer, IBM
Photo of Wei Huang
Wei Huang is interested in emerging technologies, software globalization, user experience design, and has been working as a contributor on the Dojo open source project.

Summary:  As a mature Web framework, JavaServer Faces (JSF) provides end-to-end lifecycle management and a rich component model with complete event handling and data binding. Dojo is a popular Asynchronous JavaScript + XML (Ajax) library that provides rich widgets and fancy effects for Web2.0 applications. By leveraging JSF and Dojo technologies, you can create a better user experience by using JSF integrated features on the server side and Dojo user interfaces on the client side. This article explains this process and describes how you can easily build Web applications to give your users a better experience.

Date:  03 Feb 2009
Level:  Introductory PDF:  A4 and Letter (231KB | 12 pages)Get Adobe® Reader®
Activity:  8718 views

Introduction

JSF is a popular Web framework that is quite stable and has been widely used in the Java™ Platform, Enterprise Edition (Java EE) domain. Dojo is one of the most powerful Web 2.0 libraries and gives you the ability to create rich interfaces for your Web applications.

By leveraging both technologies, you gain the strengths of both technologies. On the server side, the benefits include end-to-end lifecycle management for components, back-end bean data binding, and event handling. On the client side, you can utilize Dojo's rich widgets, live animations (such as fade and slide), and drag-and-drop. Additionally, with the Dojo framework's API support you can improve performance by putting more logic on the client side.

This article introduces the following approaches for leveraging JSF and Dojo technologies:

  • Deferred binding of JSF components and Dojo widgets on the client side
  • Building a customized JSF component to enable Dojo widgets
  • Lazy injection and parsing of JSF components as Dojo widgets

The sample JSF application in this article is composed of two pages: Create Project and Project Result. Figure 1 and Figure 2 show these two pages. To make it simple and easy to understand, the samples shown in these figures are used in all the approaches described in this article to demonstrate how to combine Dojo with a JSF application. (You can also view a larger version of Figure 1.)


Figure 1. Create project
Screen shot of the                     creating a project page

Figure 2. Project result
Screen shot of                     the project result page

The scenario is really quite simple. The user can enter some information to create a project, and when they click the submit button, the project information is displayed. So your task is to convert the input box, text area, and other JSF components into Dojo widgets.


Approach 1: Deferred binding of a JSF component and a Dojo widget on the client side

This approach is the simplest way to apply the Dojo style to the JSF component. We just use JavaScript to bind the JSF component and Dojo widget together on the client side. By using JavaScript, the data can be transferred and will be consistent between the JSF component and the Dojo widget.

First, take a look at the original JSF page. Listing 1 shows the JSF code. (The code in Listing 1 has been formatted to fit the article. Click here to see the entire code listing.)


Listing 1. Original Create Project JSF page
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://www.ibm.com/jsf/html_extended" prefix="hx"%>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>

<body>
<f:view>
     <h2>Make your JSF application Dojoable</h2>
     <h3>Create Project</h3>
     <h:form id="project">
     <table>
          <tbody>
               <tr>
                    <td>Project Name:</td>
                    <td><h:inputText id="projectName" 
                          value="#{projectFormBean.projectName}" size="5"/></td>
               </tr>
               <tr>
                    <td>Project description:</td>
                    <td><h:inputTextarea id="projectDescription" 
                          value="#{projectFormBean.projectDescription}" rows="2" 
                          cols="15"/></td>
               </tr>
               <tr>
                    <td colspan=2>
                         <h:commandButton id="button_submit" action="success" 
                          value="Submit" type="submit"></h:commandButton>
                    </td>
               </tr>
          </tbody>
     </table>
     </h:form>
</f:view>
</body>           

There are two input fields: project name and project description. These two fields are all bound with the back-end form bean projectFormBean.

Let's begin to convert this page with a Dojo style.

Step 1. Hide the JSF component and add the Dojo widget

You need to hide the original JSF components and change them to invisible, and then add the related Dojo widgets in this page. Your first step is to import the Dojo library and declare which widgets are needed, as shown in Listing 2. (The code in Listing 2 has been formatted to fit the article. For the complete code, click here.)


Listing 2. Import Dojo library and declare the widgets
<style type="text/css">
    @import url("${pageContext.request.contextPath}/script/
     dojo_lib/dijit/themes/tundra/tundra.css");
     @import url("${pageContext.request.contextPath}/script/dojo_lib/
     dojo/resources/dojo.css");
</style>
<script type="text/javascript" src="${pageContext.request.contextPath}/script/
     dojo_lib/dojo/dojo.js" 
    djConfig="parseOnLoad: true, isDebug:false"></script>

<script type="text/javascript">
   dojo.require("dojo.parser");
   dojo.require("dijit.form.ValidationTextBox");
   dojo.require("dijit.form.Textarea");
</script>            

In this sample, we use the tundra theme as the default style, so you must first import the tundra.css and the dojo.css. Next, you need to import the dojo.js file, which is the main js script file for Dojo. Finally, you must declare which widgets you want to use. In this case, we'll use validationTextBox and Textarea. Dojo uses the on-demand mechanism. It will only load the required files according to the declaration. Therefore, it can improve the application's performance and reduce the network transfer.

The next step is to change the JSF components to be invisible and add the Dojo widgets for display. The modified jsp is shown in Listing 3.


Listing 3. Change the JSF components to invisible and add the Dojo widget
<tr>
<td>Project name:</td>
<td><h:inputHidden value="#{projectFormBean.projectName}"/><f:verbatim> 
     <input type="text" name="projectName"
 dojoType="dijit.form.ValidationTextBox"
 regExp="[\w]+"
 required="true"
 invalidMessage="Invalid project name."/></f:verbatim>
</td>
</tr>
<tr>
<td>Project description: </td>
<td><h:inputHidden value="#{projectFormBean.projectDescription}"/><f:verbatim> 
 <textarea dojoType="dijit.form.Textarea" style="width:80%">
     </textarea></f:verbatim>
</td>
</tr>            

We change the original JSF components to inputHidden, but still bind with the back-end projectFormBean data. After this hidden component, we add the corresponding Dojo widgets. For the "project name" field, we use a validationTextBox to validate the input box on the client side, and for the "project description" field, we the use dojo textarea, which can be auto-expanded without a scroll bar.

Step 2. Copy data from the JSF components to the Dojo widgets during the page render phase

Earlier, we replaced the JSF components with Dojo widgets in the page, but the data in the page is not shown in the Dojo widgets. Next we will show you how to copy the data from the JSF components to the Dojo widgets.

In the render phase, we use JavaScript to extract the data from the JSF components and set it to the related Dojo widgets. Listing 4 shows the JavaScript codes.


Listing 4. Copy data from JSF components to Dojo widgets during the render phase
<script type="text/javascript">   
   function dojoInit(){
        dijit.registry.byClass('dijit.form.ValidationTextBox').forEach(function(pane){
          pane.setValue(pane.domNode.previousSibling.value);
     });
     
     dijit.registry.byClass('dijit.form.Textarea').forEach(function(pane){
          pane.setValue(pane.domNode.previousSibling.value);
     });
   }
   
   dojo.addOnLoad(dojoInit);
</script>            

In the dojoInit function you will find all the Dojo widgets queried by the class name. For each widget, you find its previous sibling node in the Dom tree, which is the JSF component. Then you extract the data from the JSF component and set it to the queried Dojo widget. In this way, you can synchronize the data between the JSF components and Dojo widgets. Finally, you use the dojo.addOnLoad function to make sure the dojoInit function is called during the render phase.

Note: When using the Dojo Toolkit, you should avoid using <body onload="..."> or window.onload because Dojo has its own initialization mechanism that uses window.onload. Using this mechanism yourself will interfere with Dojo's initialization routines. Dojo has provided dojo.addOnLoad, which lets you load functions after Dojo has finished its own initialization.

Step 3. Copy data from the Dojo widgets to the JSF components during the page submit phase

Currently, the dojo widgets can be shown with correct data in the page. But if a user changes the data, how can these changes be reflected to the corresponding back-end form bean? In this step, we show how to synchronize the data between Dojo widgets and JSF components during the page submit phase.

First, you must create a JavaScript function to copy the data from the Dojo widgets to JSF components. The code is shown in Listing 5.


Listing 5. Copy data from the Dojo widgets to JSF components
function setDojoValue(){
     dijit.registry.byClass('dijit.form.ValidationTextBox').forEach(function(pane){
          pane.domNode.previousSibling.value = pane.getValue();
     });
     
     dijit.registry.byClass('dijit.form.Textarea').forEach(function(pane){
          pane.domNode.previousSibling.value = pane.getValue();
     });
}            

This function is similar to the dojoInit function shown in Step 2. We still use the dojo query method to find all dojo widgets by a specified class name. Then, we extract the data from these Dojo widgets and set it to corresponding JSF components.

Next, we must invoke the setDojoValue function when the page submits, so we change the original submit button as shown in Listing 6.


Listing 6. Submit button combined with JavaScript function
<h:commandButton id="button_submit" action="success" 
    value="Submit" type="submit" onclick="setDojoValue();"></h:commandButton>            

We add the onclick attribute in the JSF commandButton, so when a user clicks on this button the JavaScript function will be invoked first, then the page will be submitted. In this way, we can copy all data in the Dojo widgets to JSF components before the page is submitted, and the data changes will be reflected to back-end form beans.

That is all you need to do using this approach. Figure 3 shows the new Create Project page.


Figure 3. Create project using Approach 1
Screen shot of the completed Create                     project using Approach 1

As you can see, we use a validated input box that can support client-side validation. We also use the dojo textarea, which can be auto-expanded without a scroll bar.


Approach 2: Build customized JSF component to enable the Dojo widget

JSF is a powerful Web framework. It not only provides standard user interface components for Web applications, but it is also a very flexible API that allows for user customization. In this section, we explain how to develop your own JSF components combined with a Dojo style.

Typically, the JSF component will contain the following parts:

  • UIComponent Class: A class derived from either UIComponentBase or other existing JSF components such as outputText. This class represents the core logic of the entire JSF component.
  • Render Class: This class is used for rendering the component. Typically, it will deal with how to generate the HTML codes for rendering, so it is critical for this section. We need to change this class and let it generate the Dojo-style HTML code.
  • UI Component Tag Class: This is a JSP tag handler class that lets the UI Component be used in a JSP page. It can also associate a separate render class with a UIComponent class.
  • Tag Library Descriptor File: This is a standard JavaEE JSP tag library descriptor (tld) file that associates the tag handler class with a usable tag in a JSP page.

We'll continue to use the create project scenario as an example, and we will show how to create an input text JSF component with dojo validation functionality.

First, take a look at the customized JSF tag. Listing 7 shows how to use the customized JSF tag in a Web application:


Listing 7. Using a customized JSF tag
<h:form id="project">
<table>
<tbody>
     <tr>
     <td>Project Name:</td>
     <td>
     <jsfdojo:input id="projectName" type="text" 
     invalidMessage="Invalid project name." 
     dojoType="dijit.form.ValidationTextBox" 
     dojoRequired="true" 
     regExp="[\w]+" 
     value="#{projectFormBean.projectName}"/>
     </td>
     </tr>            

Figure 4 shows the Web page using this approach. (You can also view a larger image of Figure 4.)


Figure 4. Create project – Approach 2
Screen shot showing the                     create project page using approach 2

To develop your own JSF input box with a Dojo style, you can use the existing JSF input box as a guide, so you do not need to implement your own UIComponent class; you can reuse the existing one. You do need to implement the render and tag classes, but that's not very difficult. You can inherit the existing JSF APIs and will only need to override a few functions.

Listing 8 shows the code of the tag class that is used for handling the tag in the JSP page. It also associates the UIComponent and Render class.


Listing 8. Tag class
public class InputTag extends InputTextTag {

    @Override
    public String getComponentType() {
        return "javax.faces.HtmlInputText";
    }

    @Override
    public String getRendererType() {
        return "jsfdojo.input.render";
    }

    private String dojoType;

    private String invalidMessage;

    private String regExp;

    private String type;
    
    private String dojoRequired;

    public String getDojoRequired() {
        return dojoRequired;
    }

    public void setDojoRequired(String dojoRequired) {
        this.dojoRequired = dojoRequired;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getRegExp() {
        return regExp;
    }

    public void setRegExp(String regExp) {
        this.regExp = regExp;
    }

    public String getDojoType() {
        return dojoType;
    }

    public void setDojoType(String dojoType) {
        this.dojoType = dojoType;
    }

    public String getInvalidMessage() {
        return invalidMessage;
    }

    public void setInvalidMessage(String invalidMessage) {
        this.invalidMessage = invalidMessage;
    }

    @Override
    protected void setProperties(UIComponent component) {
        super.setProperties(component);
  
        component.getAttributes().put("dojoType", dojoType);
        component.getAttributes().put("dojoRequired", dojoRequired);
        component.getAttributes().put("invalidMessage", invalidMessage);
        component.getAttributes().put("regExp", regExp);
        component.getAttributes().put("type", type);
    }

}            

The tag class is inherited from InputTextTag. You need to override some functions and add some new fields for the new attributes used in this tag.

The getComponentType function specifies the UIComponent class and the getRendererType function specifies the Render class. Therefore, if the JSF component has separated the render class, in the tag class these two functions are used to link them together.

The fields in the tag class represent the attributes that are used in the JSP tag. The setProperties function is used to store these attributes in the attribute's map of this component. This map will be used in the render class.

As described above, we will implement a separated render class. This class is responsible for generating the HTML codes for rendering in the Web page. Listing 9 shows the code of this render class.


Listing 9. The separated render class
public class InputRender extends TextRenderer {

    @Override
    public void encodeEnd(FacesContext context, UIComponent component) 
     throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        writer.startElement("input", component);
        String id = (String) component.getClientId(context);
        writer.writeAttribute("id", id, "id");
        writer.writeAttribute("name", id, "id");
        writer.writeAttribute("value", getValue(component), "value");
        
        writer.writeAttribute("type", (String) 
     component.getAttributes().get("type"), null);
writer.writeAttribute("dojoType", (String) 
     component.getAttributes().get("dojoType"), null);
writer.writeAttribute("required", (String) 
     component.getAttributes().get("dojoRequired"), null);
writer.writeAttribute("invalidMessage", (String) 
     component.getAttributes().get("invalidMessage"), null);
writer.writeAttribute("regExp", (String) component.getAttributes().get("regExp"), null);
        
        writer.endElement("input");
    }

    protected Object getValue(UIComponent component) {
        Object value = null;
        if (component instanceof UIInput) {
            value = ((UIInput) component).getSubmittedValue();
        }
        if (null == value && component instanceof ValueHolder) {
            value = ((ValueHolder) component).getValue();
        }
        if (value == null) {
            value = "";
        }
        return value;
    }
}            

The tag class is inherited from TextRenderer, so you don't need to worry about the decoding issue. All you need to do is implement the encoding output, which is implemented by the encodeEnd function. In this function, the ResponseWrite object makes it easy to generate the HTML codes. For the new adding attributes such as type, dojoType, required, invalidMessage and regExp, the value for these attributes are retrieved from the component's attribute map, which is set in the tag class.

The value of the value attribute is retrieved from the UIComponent, which is implemented in the getValue function.

After implementing the tag and render class, you also need to define the tag library descriptor file (tld file) for this JSF component. Listing 10 shows the contents of this tld file.


Listing 10. The tag library descriptor file – tld file
<taglib>
     <tlib-version>1.0</tlib-version>
     <jsp-version>1.2</jsp-version>
     <short-name>jsfdojo</short-name>
     <uri>http://jsfdojo.ibm.com</uri>

     <tag>
        <name>input</name>
        <tag-class>jsf.input.InputTag</tag-class>
        <body-content>empty</body-content>
        <description>
            
        </description>
        <attribute>
            <name>dojoType</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
            <description>
                
            </description>
        </attribute>
        <attribute>
            <name>dojoRequired</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
            <description>
                
            </description>
        </attribute>
        <attribute>
            <name>type</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
            <description>
                
            </description>
        </attribute>
        <attribute>
            <name>invalidMessage</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
            <description>
                
            </description>
        </attribute>
         <attribute>
            <name>regExp</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
            <description>
                
            </description>
        </attribute>
        <attribute>
            <name>value</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
            <description>
                Value reference expression pointing the attribute that
                will have the credit card number.
            </description>
        </attribute>
        ……
    </tag>
</taglib>            

In this tld file, the "tag class" field specifies the implemented tag class. It also describes the attributes that will be used in this tag. In this case, we add several new attributes: dojoType, dojoRequired, type, invalidMessage, and regExp. There are also many super class attributes such as id, value, and so on.

Finally, you need to register the new component in the faces-config.xml file. Listing 11 shows the register section of this component.


Listing 11. Register the JSF component in faces-config.xml
<render-kit>
<renderer>
        <description>
            Renderer customized input with dojo style
        </description>
        <component-family>javax.faces.Input</component-family>
        <renderer-type>jsfdojo.input.render</renderer-type>
        <renderer-class>
             jsf.input.InputRender
         </renderer-class>
      </renderer>
   </render-kit>            

The component family and renderer type fields are mapped exactly with the functions in the tag class. Listing 12 shows these functions.


Listing 12. Overriding component and renderer fields
@Override
public String getComponentType() {
return "javax.faces.HtmlInputText";
}

@Override
public String getRendererType() {
return "jsfdojo.input.render";
}            

That is all you need to do to implement your own JSF component. It involves a few different classes and xml implementations, but it is quite straightforward and not complicated, especially when based on the existing component. In this case, we just add some new attributes and override the encoding function to generate Dojo HTML codes.


Approach 3: Lazy injection (Post-parse the JSF component into a Dojo widget)

This approach is performed completely on the client side using JavaScript, but it's quite different from the first approach in this article. Lazy injection means injecting Dojo widget information into the JSF component after the whole page is loaded. Then, the Dojo parser is used to parse the JSF component and convert it into a Dojo widget. During the injection process, a handler may be registered, which will be invoked later when the JSF form is submitted. In this way, the precise data can be transferred back to a corresponding JSF listener on the server side. Figure 5 shows the detail workflow of this approach. (You can also view a larger image of Figure 5.)


Figure 5. Workflow of the Lazy injection approach
A diagram showing the                     process of the Lazy injection approach

Step 1: Inject Dojo widget information into the JSF component.

You may have already noticed that a JSF component is translated to HTML tags after the entire page finishes loading. For example, Listing 13 is the syntax of the JSF inputText component. After the page is loaded, the inputText component is translated to HTML tags, as shown in Listing 14.


Listing 13. Syntax of the JSF inputText component
<td>
     <h:inputText id="projectName" value="#{projectFormBean.projectName}" size="5"/>
</td>            


List 14. Generated HTML tags for the JSF inputText component
<td>
     <input type="text" name="project:projectName" id="project:projectName" size="5"/>
</td>            

So, the idea is quite straightforward. Add the necessary widget information to the generated HTML tags so the Dojo parser can recognize and parse it to a Dojo widget. Additionally, to differentiate normal HTML tags and those generated from the JSF component, a special attribute, jsf2dojo='true', is marked on the wrapper node of the JSF component. Listing 15 shows the updated JSF page for the Create Project sample. You can see more details in the NewProject_lazy_inject.jsp file.


Listing 15. Updated JSF page with jsf2dojo='true' attribute
<tr>
     <td jsf2dojo='true'>
          <h:inputText id="projectName" value="#{projectFormBean.projectName}" 
          size="5"/>
     </td>
</tr>
<tr>
     <td jsf2dojo='true'>
     <h:inputTextarea id="projectDescription" 
     value="#{projectFormBean.projectDescription}" rows="2" cols="15"/>
     </td>
</tr>            

In this way, we can mark part of the JSF components in a page and convert them into Dojo widgets; the other JSF components will remain unchanged. Please note the jsf2dojo='true' attribute is added to the wrapper node rather than the JSF component itself, because otherwise, the JSF compiler will treat it as an invalid attribute with compilation errors.

Step 2: Parse injected JSF components into Dojo widgets

After the Dojo injection is finished, the next step is to parse the JSF components into Dojo widgets. The Dojo parser is the most vital foundation for all widgets. By default, after a page is loaded the Dojo parser scans the whole page for tags with Dojo markup (for example, dojoType='xxx') and parses them into widgets. Another use of the Dojo parser is parsing tags dynamically or, as we say, lazy parsing. Here is how we do it.

All the parsing logic is encapsulated in the jsf2dojo.js file, so the first step is to include it in the JSF page, as shown in Listing 16.


Listing 16. import jsf2dojo.js
<script type="text/javascript" src="script/jsf2dojo.js"></script>            

Next, add all the parsing steps to jsf2dojo.js. The first step is the widget declaration, as shown in Listing 17.


Listing 17. Widget class declarations and page onload handler
dojo.require("dijit.form.ValidationTextBox");
dojo.require("dijit.form.Textarea");
dojo.require("dojo.parser");

var TYPE_MAP = {
     text      : 'dijit.form.ValidationTextBox',
     textarea : 'dijit.form.Textarea'
};

dojo.addOnLoad(init);            

The widget classes are declared for the Create Project sample. Here, Dojo ValidationTextBox and Textarea widgets are used. The Dojo parser is also included. The TYPE_MAP constant defines the class mappings that we'll use later. The dojo.addOnLoad(init) function registers a callback function that will be invoked after the whole page is loaded and Dojo finishes initialization.

Listing 18 shows the content of function init.


Listing 18. Parse the JSF component to a Dojo widget
function init(){          
     var jsfContainerList = dojo.query("[jsf2dojo='true']");     

     for(var i = 0 ; i < jsfContainerList.length; i++){
          var jsfWidget = _getJsfWidget(jsfContainerList[i]);//get JSF component node
          if(!jsfWidget) continue;     
               
          if('input' == jsfWidget.tagName.toLowerCase() 
          && 'text' == jsfWidget.type.toLowerCase()){ // input 
               jsfWidget.setAttribute('dojoType',
                    TYPE_MAP[jsfWidget.type.toLowerCase()]);
               jsfWidget.setAttribute('promptMessage',
                    "Please Enter your information");
               jsfWidget.setAttribute('required',"true");                    
               
          }else{
               jsfWidget.setAttribute('dojoType', 
                    TYPE_MAP[jsfWidget.tagName.toLowerCase()]);
          }
          dojo.parser.parse(jsfContainerList[i]);//parse wrapper node
       }
}            

The init function contains all the parsing logic. First dojo.query("[jsf2dojo='true']") is used to fetch all the wrappers that contain the JSF components we defined previously (<td>). Then _getJsfWidget() is used to get the exact HTML tag generated from the JSF component. For the JSF inputText component, we add dojoType=' dijit.form.ValidationTextBox' and other specific attributes, including a prompt message and whether the attribute is required. For the JSF inputTextarea component, we add dojoType=' dijit.form.Textarea'. Because everything is ready, dojo.parser.parse() is invoked to generate the Dojo widgets. Here the wrapper node (<td>) of the JSF component is used for parsing.

Step 3: Verify the page

You can verify the result by accessing http://[server ip]:[server port]/JSFSampleEarWeb/faces/NewProject_lazy_inject.jsp. You should see the Dojo widgets on the page as shown in Figure 6.


Figure 6. Create project – Approach 3
Screen shot of the                     create project page using approach 3

After you click Submit, the Project name and description fields are transferred to the JSF listener on the server side and displayed on another JSF page named Project_result.jsp.

Advanced topics

You may have noticed that the steps listed above only deal with a simple case where the JSF component has a close mapping with the Dojo widget. What if the mapping is not so simple? As mentioned previously, some extra tips are required.

First, we'll add more JSF components to the page, as shown in Listing 19.


Listing 19. JSF page with more components added
<tr>
     <td jsf2dojo='true'>
          <h:selectOneMenu id="projectOption" value="location">
          <f:selectItem id="select1" itemValue="Shanghai" itemLabel="Shanghai"/>
          <f:selectItem id="select2" itemValue="London" itemLabel="London"/>
          <f:selectItem id="select3" itemValue="New York" itemLabel="New York"/>
          </h:selectOneMenu>
     </td>
</tr>
<tr>
     <td jsf2dojo='true' jsfType='radio'>
          <h:selectOneRadio id="newPrjRadio" value="NYPrj">
                 <f:selectItem id="rd1" itemLabel="Yes" itemValue="y" />
          <f:selectItem id="rd2" itemLabel="No" itemValue="n" />
          </h:selectOneRadio>
     </td>
</tr>
<tr>
     <td jsf2dojo='true'>
          <h:selectBooleanCheckbox id="ck1" value="manager" />Project Manager
          <h:selectBooleanCheckbox id="ck2" value="technical leader"/>Techinal Leader
          <h:selectBooleanCheckbox id="ck3" value="member" /> Developer&Tester
     </td>
</tr>
<tr>
     <td jsf2dojo='true'>
          <h:commandButton id="btnSubmit" action="success" value="Submit"
           type="submit"></h:commandButton>
     </td>
     <td><input id='btnSubmit_mock' type='text' style='display:none'/></td>
</tr>            

Here, a new attribute, jsfType='radio', is added for the JSF selectOneRadio component, and a hidden input text is also added for the JSF commandButton component. These attributes are used later during the parsing process. For more details of the updated page, refer to the NewProject_lazy_inject_advanced.jsp file.

Next, you'll parse the new JSF components.

In some cases, the HTML source will become totally different after the JSF component is converted to a Dojo widget. For example, Listing 20 shows the HTML source for the JSF selectOneMenu component, and Listing 21 shows the source after selectOneMenu is converted to the Dojo ComboBox widget.


Listing 20. HTML source for the JSF selectOneMenu component
<select id="project:projectOption" size="1" name="project:projectOption">
     <option value="Shanghai">Shanghai</option>
     <option value="London">London</option>
     <option value="New York">New York</option>
</select>            


Listing 21. HTML source for the Dojo ComboBox widget
<div id="widget_project:projectOption" class="dijit ..." value="Shanghai" 
     widgetid="project:projectOption" ...>
     <div style="overflow: hidden;">
               ...
          <div class="dijitReset dijitInputField">
               <input id="project:projectOption" class="dijitReset" type="text"/>
          </div>
     </div>
</div>            

To make these differences transparent to the JSF listener on the server side, a handler will be registered and invoked when the JSF form is submitted. Listing 22 shows the updated logic to parse more JSF components to Dojo widgets.


Listing 22. Parse more JSF components to Dojo widgets
for(var n = 0; n < jsfWidgets.length; n++){
     var jsfWidget = jsfWidgets[n];
     
     if ('select' == jsfWidget.tagName.toLowerCase()){
          jsfWidget.setAttribute(DOJO_TYPE, 
               TYPE_MAP[jsfWidget.tagName.toLowerCase()]);
          mockFunc = _mockSelect;
          
     }else if('textarea' == jsfWidget.tagName.toLowerCase()){
          jsfWidget.setAttribute(DOJO_TYPE, 
               TYPE_MAP[jsfWidget.tagName.toLowerCase()]);
          
     }else if('input' == jsfWidget.tagName.toLowerCase()){
          var type = jsfWidget.type.toLowerCase();
          
          if('submit' == type){
               var params = {label: jsfWidget.value};
               var dojoWidget = new dijit.form.Button(params, jsfWidget);
               dojo.connect(dojoWidget, 'onClick', window, 'formSubmit');
               mockFunc = _mockSubmitInput;

          }else if('text' == type){
               jsfWidget.setAttribute(DOJO_TYPE, TYPE_MAP[type]);
               jsfWidget.setAttribute('promptMessage',
                    "Please Enter your information");
               jsfWidget.setAttribute('required',"true");                    
          
          }else if ('checkbox' == type || 'radio' == type){     
               jsfWidget.setAttribute(DOJO_TYPE, TYPE_MAP[type]);
          }
     }
     if(!dojoWidget){
          dojoWidget = dojo.parser.parse(jsfList[j])[0];//parse wrapper node
     }     
     if(dojoWidget && mockFunc){
          mockFuncMap.push({'dojoWidget': dojoWidget, 'mockFunc': mockFunc});
     }
}            

Callback handlers, _mockSelect and _ mockSubmitInput, are registered for the JSF selectOneMenu and commandButton. As shown in Listing 23, these handlers bind data to corresponding fields to make sure the form information remains the same in HTTPRequest. In this way, the JSF listener on the server side is unaware of any changes. Note that the Dojo Button is created for commandButton specifically.


Listing 23. Sample callback handler for JSF selectOneMenu
Function _mockSelect(dojoWidget){
     var mockedSelect = dojo.byId(dojoWidget.id);
     mockedSelect.value = (dojoWidget.item) ? dojoWidget.item.value : 
          dojoWidget.store.root.value;
}            

Basically, all callback handlers have two steps: first find the corresponding field and then add necessary values. As shown in Listing 22, by using the dojo.connect(dojoWidget, 'onClick', window, 'formSubmit') function, formSubmit is called when the JSF form is submitted. Listing 24 shows the details of formSubmit.


Listing 24. formSubmit function called before JSF form submitted.
function formSubmit(event){
        for(var i = 0 ; i < mockFuncMap.length; i++){     
          mockFuncMap[i]['mockFunc']( mockFuncMap[i]['dojoWidget'] );        
     }
     event.target.form.submit(); //event.target - submit button
}            

The only thing formSubmit does is invoke all the registered callback handlers so that the form information is correct. Everything is transparent to the JSF listener on the server side.

Now, verify the result by accessing http://[server ip]:[server port]/JSFSampleEarWeb/faces/NewProject_lazy_inject_advanced.jsp. You will see all the JSF components have now been converted to Dojo widgets, as shown in Figure 7.


Figure 7. Create project with more widgets – Approach 3
Screen shot of create                     project with more widgets using approach 3

Comparison

In the previous sections, we listed three approaches to make the combine a JSF application with Dojo. Each approach has its advantages and weaknesses. This section compares these three approaches from following aspects:

  • Complexity—Is it easy or hard to implement? Does it need to revise many files or need to modify the configuration files?
  • Applicability—Is it applicable for all JSF components? Does it have any limitations?
  • Reusability—Is it easy to reuse? Is it possible to become a stand-alone component that can be used directly by other projects?

Table 1 shows the comparison of these three approaches.


Table 1. Comparison of these three approaches
ApproachComplexityApplicabilityReusability
Approach 1 1 2 2
Approach 2 3 3 3
Approach 3 2 3 2

Note: We just use numbers from 1 to 3 to indicate the degree of each aspect. 1 means the lowest level while 3 means the highest level.

As you can see from this table, approach 1 is the easiest to be implemented, but it has limitations. This approach is not applicable for every JSF component, such as Checkbox. Therefore, approach 1 is a good way to quickly convert some JSF components (input box, textarea, and so on) to Dojo widgets.

Approach 2 is a little more complex, because building your own JSF component is not very simple. But this approach is applicable for many JSF components, and is very easy to reuse. The JSF components with Dojo can be used directly by other projects. So, approach 2 is a good choice for enterprise development, because many projects may have the same requirements.

Approach 3 is mid-level. It is not as complex as approach 2 and is applicable for many JSF components, but it is not very easy to reuse. It requires more knowledge about the Dojo widget framework. Therefore, this approach is suitable for single project development. If there is only one project that needs JSF components with Dojo, and you don't want to spend much time to implement it, approach 3 is your best choice.


Conclusion

Integrating JSF and Dojo is very useful. It leverages the JSF's server-side abilities and Dojo's powerful widgets, allowing you to easily build Web applications with better user experiences. This article provides three approaches to modify a JSF application with Dojo. Each approach has its own advantages and weaknesses, so you can choose the one most suited to your project requirements.



Download

DescriptionNameSizeDownload method
Sample codeJSFSampleEarWeb.war17KB HTTP

Information about download methods


Resources

Learn

  • Dojo Home is the official home for the Dojo open source foundation. There you can get the latest news, project status, and so on.

  • Dojo Book provides a detailed introduction for the whole Dojo library, including common usage, complete features, and customization options.

  • Dojo online is an API document that provides detailed API introductions.

  • Dojo Spotlight and demo page provide many live demos constructed on Dojo.

  • Visit the JSF project page to learn more about the JSF technology.

  • The article "JSF for nonbelievers: JSF component development" (developerworks , July 2005) is a good tutorial to guide developer to build JSF component.

Get products and technologies

  • Dojo download center provides both the latest and all previous releases of Dojo.

  • You can also get answers for any problems related with Dojo on the Dojo Forum.

About the authors

Photo of Dan Want

Dan Wang has worked in the IBM China Development Lab since 2006, and has participated in several J2EE projects. He is interested in new Web technology including Web 2.0 and mashup technologies.

Photo of Wei Huang

Wei Huang is interested in emerging technologies, software globalization, user experience design, and has been working as a contributor on the Dojo open source project.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=367760
ArticleTitle=Combine JSF with Dojo widgets to create a better user experience
publish-date=02032009
author1-email=wangdsh@cn.ibm.com
author1-email-cc=
author2-email=hwcdl@cn.ibm.com
author2-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers