Skip to main content

New Feature for JSF 1.2

Posted by jhook on February 25, 2006 at 9:03 AM PST

First off, thanks to the JCP and JEE EG for allowing us to add this feature so late in the game. The system *does* work! Now, on to the details...

The core of JSF views are mutable component trees, uniquely identified, black boxes of encapsulated features that can be dropped in and combined in the true academic definition of a 'component'. Each component can participate not only in rendering, but all other phases of MVC including updating, validation, invoking actions, etc as part of an event system. One way to look at it is Action chaining to the Nth degree in the form of widgets that will coordinate/handle MVC request processing.

So here we have trees of components. How do I ask for a particular component in the tree? Well, currently the JSF API only had UIComponent.findComponent(FacesContext, String id), which used a specialized logic of interpreting that passed String id to find the associated component. Developers immediately assumed that the ids JSF produces work for the findComponent() method. Sorry to say, they don't.

The reason why findComponent() doesn't work is in how JSF works with iterative contexts-- something traditionally done with a c:forEach, h:dataTable, or d:displayTag. If you have a parent component who will render its children a hundred times, it doesn't mean there's a hundred children. The parent component simply evaluates its one or two children 100 times, saving on memory. Makes sense doesn't it? So, now we have to uniquely identify each child for each iteration, so JSF produces ids (Client Ids) in the form of mytable:3:text, mytable:4:text, mytable:5:text, etc. You get the picture.

Now, lets say you wanted to take that identifier of mytable:4:text and operate on it later, such as re-rendering part of the page without evaluating the rest of the page. We know that UIComponent.findComponent doesn't work since it returns a UIComponent instance-- but for what row, what state-- there's only one instance in the component tree! And, findComponent knows nothing of the client identifiers your custom UIComponents produce. What do we do?

To solve this, we've added boolean UIComponent.invokeOnComponent(FacesContext faces, String clientId, ContextCallback callback). This allows, in a Chain of Responsibility fashion, a callback to be passed into the tree and when a UIComponent decides to handle it, it invokes the callback and returns true, telling the process that we're done.

This allows black box components, like UIData (h:dataTable) to allow you to operate on a specific component within a given row. Keep in mind, each iteration, just like with a for loop in Java is volatile, so by passing a callback, as in an event system, you can operate on state within a given iteration without collision.

Here's a simple example of partially rendering a JSF component model for things like AJAX. JSF renders your page and produces a series of client identifiers in the page. Now, you want to refresh only part of the rendered page for a known client identifier. Simple!

public static final ContextCallback RENDER = new ContextCallback() {
   public void invokeContextCallback(FacesContext ctx, UIComponent c) {
      c.renderAll(ctx);
   }
};

// custom ajax request
String clientId = paramMap.get("renderId");
UIViewRoot root = faces.getViewRoot();

boolean found = root.invokeOnComponent(faces, clientId, RENDER);

if (!found) throw new FacesException(clientId + " not found!");

Notice, we didn't need to process or render the whole tree and mess with special content buffering or wasteful evaluation. We just pick out what we want to operate on. The RENDER ContextCallback was a simple example, but could be extended for any number of things. Since each UIComponent knows how it produces client identifiers, you can optimize the invokeOnComponent seach to jump to the correct component state in just a few hops, avoiding visiting or evaluating 100 child component iterations when you know the client id was for index 101.

Fantastic stuff in my opinion. At the 30,000 foot view, here we've broken through the traditional URI/resource in request/action based frameworks such as RoR, where each thing you want to partially handle has to be separated out and coordinated as a specialized endpoint. Not so with JSF since we've extended the concept of a URI into the page itself, allowing you to uniquely operate on resources embeded into the page. The best part is that your components don't need to know that they are processed as part of a whole or part. In truth, the same applies to your domain model-- refresh that list of employees or modify just one employee without needing to coordinate actions or special cases.

Related Topics >>