Jim Driscoll's Blog

Notes on Technology and the Web

JSF 2, Custom Java Components, and Ajax Behaviors

with one comment

Unlike most of my blog posts, where I try to describe the easiest possible way to do things, in this posting, I’ll instead go over a Java-based custom JSF component that responds to the Ajax tag. The reason being that there simply aren’t any examples out there of how to do this, and at least two people have expressed interest in finding exactly out how this is done. I’d advise anyone considering doing this to make really sure that you can’t do the same thing in a Composite Component (you usually can), but sometimes, a Java-based custom JSF component is going to be required.

We’re going to cover the following topics here, and it’s going to be a little more code than usual, but I suspect that this will end up saving some folks a bunch of time, so lets plow forward. I’ll cover:

  • Ajax listeners
  • Facelet components
  • Integrating the two

First, the Ajax Listener

An ajax listener, connected to your ajax event with the listener attribute, is a method that will be called every time the ajax request is made. For example, let’s look at the following page section:

   1 Echo test: <h:outputText id="out" value="#{custom.hello}"/>
   2 <br/>
   3 Echo count: <h:outputText id="count" value="#{custom.count}"/>
   4 <br/>
   5 <h:inputText id="in" value="#{custom.hello}" autocomplete="off">
   6     <f:ajax event="keyup" render="out count eventcount" listener="#{custom.update}"/>
   7 </h:inputText>
   8 <br/>
   9 Event count: <h:outputText id="eventcount" value="#{custom.eventCount}"/>

We’ve got three bean properties – hello (which is the string entered by the inputText), count (which is a count of the characters in hello, and eventCount (which is a count of the number of ajax requests). We also have a method on the bean, update (line 6), which will be called every time the ajax call is submitted.

The behavior of this page is pretty simple – every time you press a character in the inputText, the complete value of the input is echoed to the outputText "out" (line 1) – the length of "out" is written to "count" (line 3), and the "eventCount" outputText (line 9) has it’s value incremented by one.

So – what code is in the bean? Here’s the relevant bits:

 

   1 public void setHello(String hello) {
   2     this.hello = hello;
   3 }
   4 public int getCount() {
   5     return count;
   6 }
   7 public int getEventCount() {
   8     return eventCount;
   9 }
  10 public void update(AjaxBehaviorEvent event) {
  11     count = hello.length();
  12     eventCount++;
  13 }

 

Not so bad – the only thing new here is that AjaxBehaviorEvent class – and we’re not even using it. The update method will simply set up the values to be correct, and we let the Ajax render to the rest. So – listeners are easy.

 

Facelets Components

Now, we’ll want to create a custom tag in Java. To do that, we’ll need to make a few configuration file entries, and write a little java code. But first, let’s see it used in the page:

In the XHTML header, we’ll say:

   1 <html xmlns="http://www.w3.org/1999/xhtml"
   2       xmlns:ui="http://java.sun.com/jsf/facelets"
   3       xmlns:h="http://java.sun.com/jsf/html"
   4       xmlns:f="http://java.sun.com/jsf/core"
   5       xmlns:cu="http://javaserverfaces.dev.java.net/demo/custom-taglib">

Setting up the "cu" prefix (line 5) to point to "custom-taglib" (the whole URL is significant). Then later on in the page, we’ll use it like so:

<cu:custom id="customId">

We then need to add an entry in web.xml:

   1 <context-param>
   2    <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
   3    <param-value>/WEB-INF/custom-taglib.xml</param-value>
   4 </context-param>

This points to our next config file, which is the filename on line 3. Here’s its contents, in full:

   1 <facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee"
   2               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   3               xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
   4               version="2.0">
   5   <namespace>http://javaserverfaces.dev.java.net/demo/custom-taglib</namespace>
   6   <tag>
   7     <tag-name>custom</tag-name>
   8       <component>
   9         <component-type>mycustom</component-type>
  10       </component>
  11   </tag>
  12 </facelet-taglib>

Note that the namespace element on line 5 matches the URL we used for the namespace in the html element of the using page. We said this taglibrary will have one tag "custom" (line 7), which maps to the FacesComponent "mycustom". Where does it find the definition of "mycustom"? In the Java file defining the component, using the new @FacesComponent attribute. Here’s the full Java code, leaving out the imports:

   1 @FacesComponent(value = "mycustom")
   2 public class MyCustom extends UIComponentBase {
   3 
   4     @Override
   5     public String getFamily() {
   6         return "custom";
   7     }
   8 
   9     @Override
  10     public void encodeEnd(FacesContext context) throws IOException {
  11 
  12         ResponseWriter responseWriter = context.getResponseWriter();
  13         responseWriter.startElement("div", null);
  14         responseWriter.writeAttribute("id",getClientId(context),"id");
  15         responseWriter.writeAttribute("name", getClientId(context),"clientId");
  16         responseWriter.write("Howdy!");
  17         responseWriter.endElement("div");
  18     }
  19 }

In fact, the Java code itself is simple enough that I don’t really think it requires any explanation. Putting the cu:custom tag in your page will now render Howdy!, surrounded by a div with the same id and name as you gave the component. All that’s left is to add the Ajax. That… is a bit more complicated, but now that we’ve handled everything else, it’s really just incremental.

Using f:ajax with your custom tag

To use the f:ajax tag, we’d like to, for instance, do something like this:

   1 <cu:custom id="customId">
   2     <f:ajax render="eventcount" listener="#{custom.updateEventCount}"/>
   3 </cu:custom>

Meaning, we’d like to just decorate the tag, and let it do something "smart". In this case, we’ll default to "onclick" (since we’re dealing with a div, after all, we could also default to "onmouseover", for instance). It’d also be nice if we could still call the ajax listener. That’ll require a bit more code. Here’s the full Java component, with the additional ajax code. I’ll go over it at the end:

   1 @FacesComponent(value = "mycustom")
   2 public class MyCustom extends UIComponentBase implements ClientBehaviorHolder {
   3 
   4     @Override
   5     public String getFamily() {
   6         return "custom";
   7     }
   8 
   9     @Override
  10     public void encodeEnd(FacesContext context) throws IOException {
  11 
  12         ClientBehaviorContext behaviorContext =
  13                 ClientBehaviorContext.createClientBehaviorContext(context, 
  14                 this, "click", getClientId(context), null);
  15 
  16         ResponseWriter responseWriter = context.getResponseWriter();
  17         responseWriter.startElement("div", null);
  18         responseWriter.writeAttribute("id",getClientId(context),"id");
  19         responseWriter.writeAttribute("name", getClientId(context),"clientId");
  20         Map<String,List<ClientBehavior>> behaviors = getClientBehaviors();
  21         if (behaviors.containsKey("click") ) {
  22             String click = behaviors.get("click").get(0).getScript(behaviorContext);
  23             responseWriter.writeAttribute("onclick", click, null);
  24         }
  25         responseWriter.write("Click me!");
  26         responseWriter.endElement("div");
  27     }
  28 
  29     
  30     @Override
  31     public void decode(FacesContext context) {
  32         Map<String, List<ClientBehavior>> behaviors = getClientBehaviors();
  33         if (behaviors.isEmpty()) {
  34             return;
  35         }
  36 
  37         ExternalContext external = context.getExternalContext();
  38         Map<String, String> params = external.getRequestParameterMap();
  39         String behaviorEvent = params.get("javax.faces.behavior.event");
  40 
  41         if (behaviorEvent != null) {
  42             List<ClientBehavior> behaviorsForEvent = behaviors.get(behaviorEvent);
  43 
  44             if (behaviors.size() > 0) {
  45                 String behaviorSource = params.get("javax.faces.source");
  46                String clientId = getClientId(context);
  47                if (behaviorSource != null && behaviorSource.equals(clientId)) {
  48                    for (ClientBehavior behavior: behaviorsForEvent) {
  49                        behavior.decode(context, this);
  50                    }
  51                }
  52             }
  53         }
  54     }
  55 
  56     @Override
  57     public Collection<String> getEventNames() {
  58         return Arrays.asList("click");
  59     }
  60 
  61     @Override
  62     public String getDefaultEventName() {
  63         return "click";
  64     }
  65 }

At 65 lines, this is probably the longest code example I’ve ever posted, but most of this is either really easy, or stuff you’ve seen in the previous section. First, we define what Ajax events we’ll accept ("click") and what one is the default ("click" again), on lines 56-64. These are part of the ClientBehaviorHolder interface (line 2). We also had to add a little code to the encodeEnd method, so that we correctly output the DOM event script as part of the div (lines 12-14, 20-24). And lastly, we needed to add a decode method, since our component is no longer output only – the ajax event handling code is always part of the decode process (lines 31-50). This is the part where we actually make sure that that listener is being called.

Did I mention that you can do pretty much the same thing in a composite component?  That’ll be the subject of a future blog.

Well, I warned you this was a little more complex – hopefully it’s all fairly clear. If it isn’t – ask in the comments.

 

(This article originally published on my java.net blog on October 9, 2009.)

Advertisements

Written by jamesgdriscoll

February 9, 2010 at 10:53 PM

Posted in ajax, JSF

One Response

Subscribe to comments with RSS.

  1. Exactly what I was looking for 🙂

    With your method we are independent of the jsf implementation (mojarra, myfaces, or…) when we need to generate a handler script in ajax case.
    But do you know if there is an identical mechanism for submit form events ?

    In java class RenderKitUtils of Mojarra i see that the method “renderOnclick” renders a handler script on the “onclick” attribut in case we have ajax or submit or a js function defined by the developer (or a composition of these elements) and also takes into account the f:param tag.
    For the ajax part, mojarra used your method as described in this article (and generates a “mojarra.ab” with AjaxBehaviorRenderer) but for the submit part it seems hard coded (generation of “mojarra.jsfcljs”)

    The goal is to avoid the hard coding in order to have independents components of the implementation, as written in your artcle we can not go use the composite method

    compoasso

    November 30, 2012 at 8:52 AM


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: