Easy Page Actions with Facelets |
It appears that everybody has come up with their own way to implement page actions in JSF. i.e. execute some code before a page is processed/rendered, like you did in Struts. Well, it turns out that I occasionally need this too (surprisingly infrequently though), so I had a look at how Shale and Seam solve the problem.
Shale uses faces-config.xml to map view ids to managed beans which implement a ViewController interface [1]. Okay, that seems alright, but it means more xml and more classes. Often I just need to put a request-scope attribute in place, so this feels like too much work to me. Seam allows you to map view ids to method expressions via a pages.xml configuration file [2]. This means no extra beans, but does mean more configuration. Can it be done with no xml and no new beans?
Yes! And the tool for the job has probably been in your toolkit all along - Facelets TagHandlers.
Easy Page Actions with FaceletsThere are two advantages to using TagHandlers to implement page actions:
The Java code for the TagHandler is very simple:
/** * This tag evaluates a method expression, so it can be used to implement * 'page actions' since it is only executed when the view is built. */ public class ActionHandler extends TagHandler { private final TagAttribute method; public ActionHandler(TagConfig config) { super(config); this.method = this.getRequiredAttribute("method"); } /** evaluate the method expression */ public void apply(FaceletContext ctx, UIComponent parent) { method.getMethodExpression(ctx, null, new Class[] {}). invoke(ctx.getFacesContext().getELContext(), null); } }
To use the action, you must map it in your facelets taglib:
... <tag> <tag-name>action</tag-name> <handler-class>furnace.core.tags.ActionHandler</handler-class> </tag>
and then put it into the template where it is needed, e.g.:
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:fc="https://rogerkeays.com/furnace/core"> <!-- merge the user into the current persistence context --> <fc:action method="${modules.forums.mergeUser}"/> <!-- then show their profile --> <h:outputText value="${user.name}"/> ... </ui:composition>
You should note though, that the TagHandlers aren't invoked on a postback, so if this is a part of your requirements you could use a PhaseListener, or the Seam or Shale approach.
This code will be available in the next version of the Furnace Webapp Framework.
[1] http://shale.apache.org/shale-view/index.html
[2] http://docs.jboss.org/seam/1.2.1.GA/reference/en/html/events.html#d0e3776
Roger Keays is an artist, an engineer, and a student of life. He has no fixed address and has left footprints on 40-something different countries around the world. Roger is addicted to surfing. His other interests are music, psychology, languages, the proper use of semicolons, and finding good food. |