The AjaxComponent strategy for JSF: The best of both worlds

Ajax-enable your components with a phase listener

1 2 3 4 5 6 7 Page 3
Page 3 of 7

The technique for packaging the response is interesting. You wrap it in XML, which the front-end JavaScript is expecting. The whole response is framed in <response> tags; in addition to the <message>, it includes a <status> element. You can see that the response can be as simple or involved as the situation merits.

If anything goes wrong, you need to prepare an appropriate response, with its status set to ERROR.

Finally, you take the XML string and send it off to the JavaScript that initiated the call. The request processing is then complete.

Notice that you did not call FacesContext.responseComplete() anywhere; that's because you know that it was called by the PhaseListener that invoked this method in the first place. You'll see that happen when you take a closer look at the PhaseListener.

Setting up the Ajax Request: The encodeBegin() method

Next, you'll see how the renderer outputs itself with the ability to make the Ajax call. This simple example only requires the encodeBegin() method, illustrated in Listing 4.

Listing 4. The Renderer: encodeBegin()

private static final String INPUT_ID = "tutorial.jsf.ajax.component.INPUT";
  private static final String INPUT_NAME = "tutorial.jsf.ajax.component.INPUT"; // This will be the text field key in the request
  private static final String BUTTON_ID = "tutorial.jsf.ajax.component.BUTTON";
  private static final String MESSAGE_DIV_ID = "tutorial.jsf.ajax.component.MESSAGE_DIV";
    private static final String CLIENT_ID = "tutorial.jsf.ajax.component.CLIENT_ID";

  //...

public void encodeBegin(FacesContext context, UIComponent component) throws IOException{
  if (log.isTraceEnabled()) { log.trace("begin encodeBegin()"); }

  HttpServletRequest request = (HttpServletRequest)context.getExternalContext().getRequest();

  AjaxComponent ajaxComp = (AjaxComponent)component;

  ResponseWriter out = context.getResponseWriter();

  String clientId = ajaxComp.getClientId(context);

  out.startElement("div", ajaxComp);
  out.writeAttribute("id", clientId, null);
  out.writeAttribute("style", "border:solid; width:200; height:200;", null);

  out.startElement("div", ajaxComp); // Message div
  out.writeAttribute("id", MESSAGE_DIV_ID, null);
  out.endElement("div"); // Message div

  out.startElement("input", ajaxComp);
  out.writeAttribute("type", "text", null);
  out.writeAttribute("id", INPUT_ID, null);
  out.writeAttribute("name", INPUT_NAME, null);
  out.endElement("input");

  out.startElement("button", component);
  out.writeAttribute("type", "button", null);
  out.writeAttribute("name", BUTTON_ID, null);
  out.writeAttribute("id", BUTTON_ID, null);
  out.writeAttribute("value", BUTTON_ID, null);
  out.writeText("Ajax It", "null");
  out.endElement("button");

  // A hidden field to hold the URL of the server for the ajax request
  out.startElement("input", ajaxComp);
  out.writeAttribute("id", "tutorial.jsf.ajax.component.SERVER", null);
  out.writeAttribute("type", "hidden", null);
  out.writeAttribute("value", request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getRequestURI(), null);
  out.endElement("input");

  // A hidden field to hold the component Client ID
  out.startElement("input", ajaxComp);
  out.writeAttribute("id", CLIENT_ID, null);
  out.writeAttribute("type", "hidden", null);
  out.writeAttribute("value", clientId, null);
  out.endElement("input");

  out.write("AjaxComponent\n");
  out.startElement("script", ajaxComp);
  out.write("dojo.addOnLoad(AjaxComponent.loadComponent());\n"); // Sets up the client side JS
  out.endElement("script");
}


Most of this code is standard JSF Renderer fare. It outputs HTML that displays what you want. Notice, however, that it's careful to give every element you're interested in a distinct name. That's what all those String constants are for. This is important, because you're going to need to find these elements again in the client-side JavaScript.

Notice also that one bit of information sent is the URL of the server; this tells your JavaScript where to send its eventual Ajax call. Another piece of information is the JSF clientId. You will see that this is used later by the PhaseListener. Both of these are written to hidden fields, where the user can't see them, but they are available to the JavaScript.

This is the critical line:

out.write("dojo.addOnLoad(AjaxComponent.loadComponent());\n");

This code uses the Dojo JavaScript library to add an onLoad listener that calls a method once the page has loaded. Note that the component requires that the JavaScript function AjaxComponent.loadComponent() be available! In other words, the page using the AjaxComponent tag must include the JavaScript file with the AjaxComponent JavaScript.

There are more sophisticated means of incorporating JavaScript (and CSS) files into the component that avoid the JSP include. If you are using MyFaces, you can use the AddResource mechanism, but be aware that this will couple your component to the MyFaces implementation. Another strategy you might be interested in is the use of Weblets. For simplicity's sake, though, this example will just include the JavaScript file in the JSP.

1 2 3 4 5 6 7 Page 3
Page 3 of 7