Dan is an open source advocate, community catalyst, author and speaker. He's currently pursuing these interests as a Principal Software Engineer at Red Hat. In that role, he serves as a JBoss Community liaison, contributes to several JBoss Community projects, including Arquillian, ShrinkWrap, Seam 3 / DeltaSpike and JBoss Forge, and participates in the JCP on behalf of Red Hat. Dan is the author Seam in Action (Manning, 2008), writes for IBM developerWorks, NFJS magazine and JAXenter and is an internationally recognized speaker. He's presented at major software conference series including JavaOne, Devoxx, NFJS, JAX and Jazoon. After a long conference day, you'll likely find Dan enjoying tech talk with fellow community members over a Belgian Trappist beer. Dan has posted 5 posts at DZone. You can read more from them at their website. View Full User Profile

Introducing JSF 2 Client Behaviors

02.22.2010
| 83901 views |
  • submit to reddit

Default Events

As we’ve seen above, explicitly specifying an event is not always necessary.  The following eventless <f:ajax> usage is valid:

  <h:commandButton>
<f:ajax/>
</h:commandButton>

 

How is the event name determined in this case?  The ClientBehaviorHolder contract comes into play again with this method:

  public String getDefaultEventName()

 

When attaching a client behavior, if no event name is specified by the page author, the getDefaultEventName() method is consulted.  If a non-null event name is returned, the client behavior is automatically registered using this name.  If, however, the page author does not specify an event name and no default name is provided by the ClientBehaviorHolder implementation, the behavior is not registered and an error is reported.

The standard JSF HTML components leverage the default event name mechanism in two cases.  All of the standard command components specify action as the default event name, while the standard editable value holder components specify valueChange as their default event.  This allows these event names to be omitted, simplifying the page author's job for these very common cases.

 

A Simple Example

Now that we have had an introduction to the basics of the client behavior API, let's take a look at the steps involved in creating a custom client behavior.  We'll revisit our earlier example: a confirmation behavior that prompts the user before performing some action.

The process of creating a custom client behavior requires three steps. Let's review them.

 

Step 1: Implementing the Behavior

 The simplest approach to defining your own behavior is to extend the ClientBehaviorBase base class and implement a single method: getScript().  For our confirmation behavior we have an intentionally trivial getScript() implementation.  We simply return a script that calls the JavaScript confirm() API:

public class ConfirmBehavior extends ClientBehaviorBase
{
@Override
public String getScript(
ClientBehaviorContext behaviorContext) {
return "return confirm('Are you sure?')";
}
}

 

A more interesting getScript() implementation might build a script dynamically based on information specified in the ClientBehaviorContext and client behavior-specific properties.

 

Step 2: Registering the Behavior

Before we can use our client behavior, the implementation must be registered with JSF.  You might expect that this registration requires an entry to faces-config.xml:

<faces-config>
<behavior>
<behavior-id>foo.behavior.Confirm</behavior-id>
<behavior-class>
org.example.foo.behavior.ConfirmBehavior
</behavior-class>
</behavior>
</faces-config>

 

While such explicit XML-based configuration is indeed supported, fortunately it's not required.  Annotation-based configuration in JSF has finally arrived!  We can register our client behavior implementation using the @FacesBehavior annotation:

@FacesBehavior("foo.behavior.Confirm")
public class ConfirmBehavior extends ClientBehaviorBase

 

This achieves the same result without the verbose faces-config.xml entry.  The client behavior is registered with JSF under the id specified by annotation value, making it available for use within the application.

In JSF 2, annotation-based configuration can be used for registering not just client behaviors, but for registering other JSF artifacts (e.g., components, converters, validators, renderers, etc.) as well.

 

Step 3: Defining the Tag Library

We still have one explicit configuration step that must be completed before we can use our custom client behavior.  The behavior must be exposed via a Facelets tag library.  This configuration is fairly straightforward: 

<?xml version='1.0' encoding='UTF-8'?>
<facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee" version="2.0">
<namespace>http://example.org/foo</namespace>
<tag>
<tag-name>confirm</tag-name>
<behavior>
<behavior-id>foo.behavior.Confirm</behavior-id>
</behavior>
</tag>
</facelet-taglib>

 

We must specify three pieces of information:

  1. The namespace of the tag library
  2. The name of the tag
  3. The id of the client behavior

 

The taglib.xml file can be placed either in the web application’s WEB-INF directory or in the META-INF directory of the jar that contains our tag library.

 While JSF 2 greatly reduces the need for XML-based configuration, this is one case where explicit configuration is still required.   Perhaps this is an area where JSF can be further simplified in the next specification version.

 On a positive note, as is the case with components, converters and validators, Facelets provides a default handler for client behaviors.  This means that we do not need to go to the trouble of implementing a custom tag handler. Facelets automatically takes care of this bit of legwork for us.

 

Breaking Away From The Client

Like our sample <foo:confirm> behavior, behaviors often generate scripts that are limited entirely to client-side interaction. However, behaviors may also break outside of the client by issuing requests back into the Faces life cycle running on the server (i.e., postback).  The ability for behaviors to reach across tiers in this way greatly expands the set of possible use cases that can be targeted.

As an example of a cross-tier use case, let's consider an auto-suggest behavior that enhances input components with the ability to provide the user with a list of potential completions.  The usage for such a behavior might look something like this:

  <h:inputText value="#{someValue}">
<foo:suggest suggestions="#{suggestions}"/>
</h:inputText>

 

The <foo:suggest> behavior binds to a collection of valid suggestions.  As the user types into the input control, the behavior added by <foo:suggest> can make calls using jsf.ajax.request() to fetch matching suggestions from the server.  These suggestions can then be displayed for selection by the end user.

 This solution assumes that some code on the server is available to service the incoming Ajax request and produce a response containing valid suggestions.  What are our options for handling these requests? 

 The <h:inputText> participates in  decoding and, as such, could participate in the processing of the suggestion requests.  However, due to the loose coupling between components and client behaviors mentioned earlier, <h:inputText> has no intimate knowledge of the <foo:suggest> behavior and is not in a position to produce a suggestion-specific response

 Perhaps a PhaseListener could be deployed to sniff out the suggestion request?  While this is an option, it requires coordination between the <foo:suggest> behavior and the phase listener, not to mention registration of the phase listener.  Ideally, the <foo:suggest> behavior should be self-sufficient.  That is, the behavior should be able to service its own requests without requiring the assistance of any external objects or configuration. Surely there must be a simpler way that is more fine-grained and requires less configuration. Fortunately, there is!

The ClientBehavior contract exposes one additional method to specifically address this use case:

    public void decode(FacesContext context,
UIComponent component);

 

The ClientBeahvior.decode() method allows client behavior implementations to participate in request decoding. With this hook into the Faces life cycle, it's possible for a client behavior not only to post back to the server, but also to service the request itself.  Our <foo:suggest> behavior can leverage this method to service its own requests in an autonomous manner.

 

Extending Event Handling Across Tiers

The ability for behaviors to implement cross-tier processing also comes in handy for calling out to application-specific logic.  In the typical command Ajax case, application logic can be hosted in an action listener.

  <h:commandButton actionListener="#{someBean.doAction}">
<f:ajax/>
</h:commandButton>

 

The action listener is invoked regardless of whether the action event is sent via an Ajax request or a traditional form post.  This works well for cases where an ActionEvent is delivered.  However, we might also issue an Ajax request in response to client events that do not trigger actions, such as a mouseover event:

  <h:commandButton actionListener="#{someBean.doAction}">
<!-- Fetch tooltip data over Ajax -->
<f:ajax event="mouseover"/>
</h:commandButton>

 

Fetching the tooltip data in response to a mouseover client event does not cause an ActionEvent to be delivered and as such does not invoke the command component’s action listener.  We need some other way to invoke application logic in such scenarios.

<f:ajax> leverages the ClientBehavior.decode() method to provide a solution.  The application can register an event-specific server-side listener using <f:ajax>'s listener attribute:

  <f:ajax event="mouseover"
listener="#{someBean.doMouseOover}"/>

 

During decoding, the<f:ajax> behavior detects requests issued by its own client-side script and calls back the application-specified listener if present.

This decode mechanism allows client-side event handling to be extended across tiers in a generic manner. Any client-side event can now be propagated back to the server where it can be processed both by the client behavior implementation as well as by application-specified logic.

 

Conclusion

Hopefully by now you are ready to not only start using <f:ajax>, but to create your own custom client behavior implementations as well.  One of the goals of providing an extensible API is to encourage JSF users to leverage this for their own needs.   While we only have a single standard behavior (<f:ajax>) today, we expect to see many third party behavior implementations in the days to come.  Some of these behaviors may be candidates for inclusion in future versions of the JSF specification – perhaps even yours!

In this article, you learned how client behaviors can be added to existing UI components. There's one behavior you don't have to write a behavior component for at all. The Bean Validation integration in JSF 2 can automatically register validators on input components by applying the Bean Validation constraints defined on the mapped bean property. You'll learn about this tremendously convenient integration in the next installment in this series.

Published at DZone with permission of its author, Dan Allen.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Sebastian Herold replied on Tue, 2010/03/09 - 9:58am

I tryed to code your suggestion example but to assign my JS-code to multiple events, I had more than one occurence of the tag:
<foo:suggest event='blur' suggester='#{suggester}' />
<foo:suggest event='keyup' suggester='#{suggester}' />
....

Is it possible to assign script code to multiple events without more than one tag and without to set the event-attribute?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.