JSF 2 fu: After-the-fact Ajax composite components

Let page authors add Ajax to your composite components

Java™Server Faces (JSF) 2 Expert Group member David Geary continues his article series offering in-depth coverage of JSF 2 technology. In this installment, you'll learn how to let page authors add Ajax to your composite components, taking a close look at a powerful — but entirely undocumented — JSF 2.0 tag. And you'll see how to implement a reusable, general-purpose, Ajax-capable icon component in fewer than 25 lines of XML.

Share:

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.



01 June 2010

Also available in Chinese Russian Japanese

About this series

The JSF 2 fuseries, 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.

In the preceding installment of JSF 2 fu, I discussed the implementation of an autocomplete composite component with built-in Ajax. Page authors use the component in a facelet, and the component takes care of all the Ajax details. Built-in Ajax has its place, but it's also convenient to let page authors add Ajax to a composite component after (perhaps long after) the developer has implemented the component. This article shows how your composite components can accommodate after-the-fact Ajax.

As I discussed in "JSF 2 fu, Part 3: Event handling, JavaScript, and Ajax," JSF 2's <f:ajax> tag lets page authors add after-the-fact Ajax to JSF 2's built-in components. For example, with <f:ajax> you can easily turn a submit button into an Ajax button:

<h:commandButton value="Click me">
  <f:ajax>
</h:commandButton>

JSF 2's <f:ajax> tag, however, doesn't work — without a little coaxing — with composite components, because composites are simply component containers.

For example, in "JSF 2 fu, Part 2: Templating and composite components," I introduced a simple icon composite component that consists of a link, represented by an image. When a user clicks on the icon, the link submits the surrounding form, which triggers a server-side action listener associated with the icon's link. Using an icon is simple:

<util:icon image="...">
  <f:actionListener for="link" type="...">
</util:icon>

Because you can turn submit buttons into Ajax buttons with the <f:ajax> tag, you might think you could do the same for icons:

<util:icon image="...">
  <f:ajax>
  <f:actionListener for="link" type="...">
</util:icon>

The preceding code fragment doesn't work because I'm attaching the <f:ajax> tag to the icon component; what I really want to do is attach it to the link inside the icon.

What I need in this example is a mechanism that lets me attach Ajax behavior to the link inside the icon, or more generally, lets me attach Ajax behavior to components that reside inside composite components. That mechanism — implemented in Mojarra and Apache MyFaces and entirely undocumented in JSF 2.0 — is the focus of this article. (Note: MyFaces support was added as this article was written.) Before you see how the mechanism works, I'll create a new icon composite component to use it with.

Running the sample code

The code for this series is based on JSF 2 running in an enterprise container, such as GlassFish or Resin. See "JSF 2 fu: Ajax components" for a step-by-step tutorial on installing and running the code for this series with GlassFish. See Download to get the sample code for this article.

A reusable icon component

Imagine you have the coolest job in the world. Maybe you're implementing the next-generation World of Warcraft graphics engine. But alas, not today. Today, you are implementing the font selector shown in Figure 1:

Figure 1. Selecting a font
Font selector

Your bosses ask you how long it will take to implement such a thing. They want + and - icons that users can click on to change the font size in the two-character preview. Of course, they want Ajax so that the preview and the size display next to the icons update dynamically without disturbing the rest of the page.

Your bosses hope for a simple font-selector component, but you know better. You're going to implement a general-purpose icon component that can be configured at run time with an image and an action, and is fully Ajaxifiable, and then you'll use your icon component in the font preview. That way, you'll have a useful icon component that will come in handy in the future.

Now I'll show you how to implement that icon component in fewer than 25 lines of XML.


The font-selector example

Composite components: The basics

If you're unfamiliar with using or implementing JSF 2 composite components, you can find an introduction in "JSF 2 fu, Part 2: Templating and composite components."

The font-selector example consists of four files:

  • The page shown in Figure 1, defined in index.xhtml.
  • The icon component, which resides in /resources/util/icon.xhtml.
  • A listener (com.clarity.FontSelectionListener.java).
  • A bean (com.clarity.FontSettings).

Figure 2 shows the directory structure:

Figure 2. The font-selector example's files
Directory structure

Listing 1 is the facelet — index.xhtml — for the page shown in Figure 1:

Listing 1. The facelet (/web/index.xhtml)
<?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:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html" 
      xmlns:corejsf="http://corejsf.com"
      xmlns:util="http://java.sun.com/jsf/composite/util">

   <h:head>
      <h:outputStylesheet library="css" name="styles.css"/>
      <title>#{msgs.windowTitle}</title>
   </h:head>
   
   <h:body> 
      <h:outputStylesheet library="css" name="styles.css"/>
      
      <h:outputText value="#{msgs.fontSizeHeading}"
        style="padding-left: 30px; font-size: 2em;"/>
      
      <h:panelGrid columns="3" style="padding-left: 80px;">
        <util:icon id="minus" image="#{resource['images:minus.gif']}">
          <f:actionListener for="link" type="com.clarity.FontSelectionListener"/>
        </util:icon>

        <util:icon id="plus" image="#{resource['images:plus.gif']}">
          <f:actionListener for="link" type="com.clarity.FontSelectionListener"/>

        </util:icon>
        
        <h:outputText id="readout" value="#{fontSettings.size}em"/>
      </h:panelGrid>
            
      <h:outputText id="fontPreview" value="Aa" 
                 style="font-size: #{fontSettings.size}em; font-style: italic"/>

   </h:body>
</html>

The facelet in Listing 1 declares a namespace for the icon component and uses the component in the page. This is Using JSF 2.0 Composite Components 101, covered in detail in "JSF 2 fu, Part 2: Templating and composite components."

Notice that both icons are outfitted with an action listener for the icon's link component. When a user clicks on the icon's link, JSF invokes that listener, shown in Listing 2, on the server.

Listing 2. The listener (com/clarity/FontSelectionListener.java)
package com.clarity;

import javax.el.ELResolver;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;

public class FontSelectionListener implements ActionListener {
   @Override
   public void processAction(ActionEvent event)
         throws AbortProcessingException {
    FacesContext c = FacesContext.getCurrentInstance();
    ELResolver elResolver = c.getApplication().getELResolver();
    FontSettings fs = (FontSettings) 
      elResolver.getValue(c.getELContext(), null, "fontSettings");
    
    if (((UIComponent)event.getSource()).getClientId().startsWith("minus"))
      fs.decrement();
    else
      fs.increment();
   }
}

In Listing 2, I look to see if the client identifier of the component that triggered the event is minus; if so, I know that the user clicked the minus icon, and I decrement the font size. Otherwise, I increment the font size.

Notice that the listener obtains a reference to the fontSettings managed bean. It does that by getting a reference to the Expression Language Resolver, which knows how to find managed beans, given their names. The fontSettings bean is shown in Listing 3:

Listing 3. The font-settings bean (com/clarity/FontSettings.java)
package com.clarity;

import java.io.Serializable;

import javax.inject.Named; 
import javax.enterprise.context.SessionScoped; 

@Named
@SessionScoped
public class FontSettings implements Serializable {
   private static int INCREMENT = 1;
   private int size = 1;
   
   public int getSize() { return size; }
   public void setSize(int newValue) { size = newValue; }

   public void increment() { size += INCREMENT; }
   public void decrement() { size -= INCREMENT; }
}

The preceding three listings show all the code in the application except for the icon composite component. That's up next.


Implementing the icon composite component

Icons have three requirements:

  • The image must be configurable.
  • The action when the user clicks on the image must be configurable.
  • The icon must support Ajax.

In Listing 4, I fulfill the first two requirements:

Listing 4. The <util:icon> composite component, take 1 (/resources/util/icon.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="image" required="true"/>
      <composite:actionSource name="link"  targets="#{cc.clientId}:iconForm:link"/>
    </composite:interface>

    <composite:implementation>
      <div id="#{cc.clientId}">
         <h:form id="iconForm">
            <h:commandLink id="link" immediate="true">
              <h:graphicImage value="#{cc.attrs.image}"/>
            </h:commandLink>
         </h:form>
       </div>
    </composite:implementation>    
</html>

The icon component in Listing 4 declares an image attribute and an actionSource named link. That actionSource is used in Listing 1 as the value of the <f:actionListener>'s for attribute. If that's not entirely clear, you can read more about how action sources work with composite components in "JSF 2 fu, Part 2: Templating and composite components," which briefly discusses an icon implementation similar to the one in Listing 4.

The icon component implementation in Listing 4 lets the page author configure how icons look and behave, but it doesn't let the author attach Ajax behaviors to the component. As implementation now stands, if the user clicks on an image, JSF makes a full page submit and completely redraws the page when it returns.

Now you'll see how you can let page authors add Ajax to the icon component.


Adding Ajax support with <composite:clientBehavior>

To let page authors add Ajax to the link that's inside the <util:icon> component, I use the <composite:clientBehavior> tag, as shown in Listing 5:

Listing 5. The <util:icon> composite component, take 2
<!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="image" required="true"/>
      <composite:actionSource   name="link"  targets="#{cc.clientId}:iconForm:link"/>
     <composite:clientBehavior name="click" 
                              event="action"
                            targets="#{cc.clientId}:iconForm:link"/>
    </composite:interface>

    <composite:implementation>
      <div id="#{cc.clientId}">
         <h:form id="iconForm">
            <h:commandLink id="link" immediate="true">
              <h:graphicImage value="#{cc.attrs.image}"/>
            </h:commandLink>
         </h:form>
       </div>
    </composite:implementation>    
</html>

Undocumented <composite:clientBehavior>

The <composite:clientBehavior> tag was fleshed out by a subgroup of the JSF 2.0 Expert Group. It was implemented in the JSF Reference Implementation (Mojarra), but it did not get documented for JSF 2.0; therefore, you won't find any mention of it in the Javadocs or in the JSF specification.

The missing documentation is known as a spec bug (see Resources for a link to more information). The JSF Expert Group will make sure that the bug is fixed by adding documentation to the specification and Javadocs.

The <composite:clientBehavior> tag exposes an Ajax event for a component contained in a composite component. In Listing 5, I declare a client behavior with the logical name click that's associated with a real event — the action event fired by the icon's link. Here's a summary of the valid attributes for the <composite:clientBehavior> tag:

  • name: The event name used by page authors.
  • default: Either true or false. If it's true, the event specified with the name attribute is the default event.
  • event: The actual name of the event.
  • targets: A space-separated list of component IDs to which JSF retargets the behavior.

Now page authors can attach Ajax behaviors to the icon component, as shown in Listing 6:

Listing 6. The facelet, take 2
<?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:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html" 
      xmlns:corejsf="http://corejsf.com"
      xmlns:util="http://java.sun.com/jsf/composite/util">

   <h:head>
      <h:outputStylesheet library="css" name="styles.css"/>
      <title>#{msgs.windowTitle}</title>
   </h:head>
   
   <h:body> 
      <h:outputStylesheet library="css" name="styles.css"/>
      
      <h:outputText value="#{msgs.fontSizeHeading}"
        style="padding-left: 30px; font-size: 2em;"/>
      
      <h:panelGrid columns="3" style="padding-left: 80px;">
        <util:icon id="minus" image="#{resource['images:minus.gif']}">
          <f:ajax event="click" render=":readout :fontPreview"/>
          <f:actionListener for="link" type="com.clarity.FontSelectionListener"/>
        </util:icon>

        <util:icon id="plus" image="#{resource['images:plus.gif']}">
          <f:ajax event="click" render=":readout :fontPreview"/>
          <f:actionListener for="link" type="com.clarity.FontSelectionListener"/>
        </util:icon>
        
        <h:outputText id="readout" value="#{fontSettings.size}em"/>
      </h:panelGrid>
            
      <h:outputText id="fontPreview" value="Aa" 
                 style="font-size: #{fontSettings.size}em; font-style: italic"/>

   </h:body>
</html>

Rendering components outside of a composite component

In Listing 6, I specify the readout and fontPreview components for the render attribute of the <f:ajax> tag. Notice that I preface those component identifiers with a colon.

That colon forces JSF to search for the components from the top of the component hierarchy; otherwise, JSF searches from the naming container (usually a form) closest to the component to which the Ajax is attached. In the icon's case, that component is the icon's link, and the form is the icon's form that contains the link. If you remove the colons, JSF starts searching for the readout and fontPreview components from the icon's form; it won't find them, which produces an error.

In Listing 6, I've added an <f:ajax> tag to the icons. When a user clicks on one of the icons, JSF makes an Ajax call to the server, and when the call returns, JSF renders the readout and fontPreview components.

If I choose, I can add a default="true" attribute to the <composite:clientBehavior> tag in the icon's interface. That does away with the need for the page author to specify the click event, reducing the <f:ajax> tags in Listing 6 to <f:ajax render=":readout :fontPreview">.


Conclusion

In this article, I've shown you how JSF 2 makes it easy, albeit with an undocumented tag, to let page authors add Ajax functionality to your composite components. Indeed, as you've seen, you can give page authors the ability to turn composite components that make full HTTP requests into components that make Ajax requests instead. And all of that is the result of a single line of XML in a composite component's interface.

In the next installment of JSF 2 fu, I will shift gears away from composite components to take a look at using Contexts and Dependency Injection (CDI) with JSF 2.


Download

DescriptionNameSize
Sample code for this articlej-jsf2fu-0610-src.zip11KB

Resources

Learn

Get products and technologies

  • JSF: Download JSF 2.0.

Discuss

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=493550
ArticleTitle=JSF 2 fu: After-the-fact Ajax composite components
publish-date=06012010