Insights into Java Server Faces

2010/10/17

Wrapping PartialViewContext – or howto automatically add clientIds to render on AJAX requests

Filed under: JSF — Hanspeter @ 19:39

Imagine a JSF 2.0 based input view working with AJAX and you have h:messages positioned above of this input form – and you want the messages area to be rerendered on each validation step. You now will have to add the clientId of h:messages to all f:ajax render=”..” behavior definitions – or somehow add the clientId of h:messages automatically to the list of components to be rendered on partial requests. This article will show how to implement the automatic adding of a clientId to the list of components to render on partial view requests.

AJAX requests in JSF 2.0 are processed using a PartialViewContext holding the information of which components to execute and render (and others which do not matter for this article). Now we want automatically add some clientId’s to the list of components to re-render – that is add a clientId to the renderIds attribute of PartialViewContext. A possible solution would be to use a PhaseListener to add a clientId to the renderIds list e.g. in the beforePhase() handling of the render-response phase. Problem with that is, you don’t know if the clientId you add is really present on the current component tree. Therefore we go for another approch: wrapping PartialViewContext.

Wrapping PartialViewContext allows to intercept calls to getRenderIds() – which first hapens after the component tree was (re-)built in the render response phase. At this time we can check if components are present in the current component tree. Therefore I used the bindings-bean as shown in a previous post to bind e.g. the h:messages component with binding=”#{bindings.messages}”. This allows my CustomPartialViewContext to get access to the component whose clientId is to add to the renderIds list depending on the existing binding.

That is the concept – now let’s have a look at an implemenation example. Wrapping PartialViewContext is a straight forward task – same as wrapping any other contexts in JSF:

  1. implement CustomPartialViewContext.java
  2. implement CustomPartialViewContextFactory.java
  3. configure CustomPartialViewContext in faces-config.xml partial-view-context-factory element.

CustomPartialViewContextFactory simply wraps the standard PartialViewContext with our CustomPartialViewContext.

public class CustomPartialViewContextFactory extends PartialViewContextFactory {
  private PartialViewContextFactory wrapped;
  public CustomPartialViewContextFactory(PartialViewContextFactory delegate) {
    this.wrapped = delegate;
  }
  @Override
  public PartialViewContext getPartialViewContext(FacesContext context) {
    PartialViewContext partialViewContext = getWrapped().getPartialViewContext(context);
    return new CustomPartialViewContext(partialViewContext);
  }
  @Override
  public PartialViewContextFactory getWrapped() {
    return wrapped;
  }
}

On first call to CustomPartialViewContext.getRenderIds() in render-response phase the additional clientId is added to the clientIds list if the respective component is present on current component tree, isRendered() is true and that clientId is not already contained in the list of components to render.

  @Override
  public Collection<String> getRenderIds() {
    if (!added) {
      FacesContext jsf = FacesContext.getCurrentInstance();
      if (PhaseId.RENDER_RESPONSE == jsf.getCurrentPhaseId()) {
        Bindings bindings = Bindings.getInstance(jsf);
        // h:messages may be bound using binding="#{bindings.messages}"
        UIComponent component = bindings.get("messages");
        if (component != null && component.isRendered()) {
          String clientId = component.getClientId(jsf);
          Collection<String> renderIds = getWrapped().getRenderIds();
          if (!renderIds.contains(clientId)) {
            renderIds.add(clientId);
          }
        }
      }
    }
    return getWrapped().getRenderIds();
  }

Configure CustomPartialViewContextFactory using faces-config.xml as follows:

  <factory>
    <partial-view-context-factory>ch.dueni.insight2jsf.context.CustomPartialViewContextFactory</partial-view-context-factory>
  </factory>

Luckily with JSF 2.0 h:messages always renders a div with id if h:messages has an id set. This allows to update the messages area with AJAX calls. An example of how to automatically re-render the messages area using the above CustomPartialViewContext will look as simple as:

<h:messages id="msgArea" bindings="#{bindings.messages}" />

That was easy – and useful, right? And of course you can use the same logic for automatic re-rendering of other areas. You find the sources in a small project on https://github.com/dueni/faces-ext/tree/master/insight2jsf-posts.

Hope you like this

By dueni.

About these ads

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

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

The Shocking Blue Green Theme Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: