How to Use ADF Client-Side Components: Kaleidoscope ’09 Report III

Two weeks ago, I compared a pair of talks I saw at ODTUG Kaleidoscope 2009, “That’s Rich! Putting a smile on ADF Faces,” by Lucas Jellema, and “Fusion Design Fundamentals,” by Duncan Mills. In particular, I contrasted the two opinions given of ADF Faces RC client-side (that is, Javascript) programming, and came down on Lucas’ side: Adding Javascript to ADF Faces RC applications, though it shouldn’t be overdone, can be very useful, and the usual risks attendant on Javascript programming are significantly mitigated if you develop exclusively to the ADF Faces RC Client-Side API (rather than attempting direct access to/manipulation of the DOM) and understand what validation in Javascript can and can’t do (provide convenience for the user and protection against honest user error and provide real enforcement of data integrity, respectively).

What I didn’t get a chance to do in that post was talk about the actual tips for client-side component manipulation that Lucas provided. I’m going to do this over the next couple of weeks. This week, I’m going to talk about the essentials for doing any client-side component manipulation. Next week, I’ll talk about some specific component manipulation use cases that Lucas went over in his talk.

Lucas went through the essential procedure, also documented (under a fairly misleading title) here for manipulating ADF Faces RC components on the client. Here it is:

Add a Client Listener to the Component that Fires the Event

The component which should trigger the change (in itself or elsewhere) needs to have a clientListener component added to it, like so:

<af:inputText id="clientEventFiringInputText">
   <af:clientListener method="someJavascriptFunction" type="keyPress" />
</af:inputText>

The “method” attribute of the client listener should be set to the name of a Javascript function that you will write to contain the exciting code you want to fire. The “type” attribute can be any standard Javascript event, plus some component-specific events; you can find a complete list here. This lets you fire the function on any of these events. You could even add multiple clientListeners to the same component, set to fire on different events (when the value changes, when the field gains focus, etc.).

Prepare Components that Will Be Affected by the Event

If you’re going to manipulate a component in Javascript, you need to create a client-side component for it. A client-side component is a Javascript representation of the component, providing an extensive, structured, and cross-browser (though, alas, not very well documented) Javasctipt API for manipulation. ADF Faces RC creates some client-side components automatically, but the exact components and situations for which it does this are not part of the API’s contract, so you’re safer manually ensuring that they get created. You can do this by setting the “clientComponent” attribute of pretty much any UI component to “true”:

<af:outputText id="clientEventReceivingOutputText" clientComponent="true" />

Write Your Javascript Function

Finally, you need to write your Javascript function that your firing component will call. I recommend writing these functions in a separate JS file and attaching them using a resource component:

<af:resource type="javascript" source="/scripts/myPageScripts.js" />

The function should take a single argument. When your firing component calls the function, it will pass an AdfBaseEvent as the parameter (actually, my guess is that the object will always be an AdfComponentEvent, but I can’t verify that in general, so I’ll recommend only assuming AdfBaseEvent functions are available unless you’ve verified this for your specific case).

You’ll also, of course, need access to the component that fired the event, and any component you want to use/change. To get the event’s , you can just call getSource() on the parameter, and to get another component (for which you’ve generated a client component as above), you need to pass an absolute or relative path (more on that in a second) to the method findComponent() that the source object will provide. Observe:

function someJavascriptFunction(event) {
    var firingInputText=event.getSource();
    var affectedOutputText=source.findComponent("clientEventReceivingOutputText");
    // Code manipulating these client-side objects
}

So, what’s did I mean by absolute or relative path? Well, first you need to understand that some components in ADF Faces RC (or indeed, generic JSF) are what are called naming containers–which are sort of to other components what directories are to files. There doesn’t seem to be an exhaustive list anywhere of the ADF Faces RC naming containers, so you’ll need to look at the documentation for each component individually (components which are naming containers say so right up at the top of a help page).

Once you’ve figured out where your components are relative to each other and their naming containers, you can figure out the path to the target component; it’s exactly like the path that would be used (on the server side) by UIComponent.findComponent().

The next, step, of course, is to actually write the code for manipulating the client-side objects. I’ll write about that next week.