JSF 2 fu

Best practices for composite components

Implement extensible custom components

Content series: This content is part # of # in the series: JSF 2 fu Stay tuned for additional content in this series. This content is part of the series: JSF 2 fu Stay tuned for additional content in this series.

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:

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

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.

buttons sitting to the right of and 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.

button next to and enters into a text-input area. A 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

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 Related topics). 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 Related topics). 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

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

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

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.

Downloadable resources

Related topics