Sunday, October 31, 2010

Metawidget Neat Trick: Configuring JSF Programmatically

Here's a third neat trick I've discovered while using Metawidget, the Java form generator, in my own work.

Say you need to support multiple configurations for multiple Metawidgets in your app. For example, you may need one Metawidget on your page for inspecting properties but not actions, and another for inspecting just actions. The latter might be placed in a toolbar or something.

You can see a working example of this in the JSF PenguinColony Demo. It defines 2 configurations, metawidget.xml and metawidget-action.xml, and chooses between them using Metawidget's config attribute:

<m:metawidget value="#{penguin.current}"/>
<m:metawidget value="#{penguin}" config="metawidget-action.xml"/>

But there is another way: using JSF's binding attribute. Here you can defer all configuration into Java code - just as you might with, say, SwingMetawidget or AndroidMetawidet:

<m:metawidget binding="#{penguin.metawidget}"/>

Then you have the full power of Java to make decisions around how to configure each Metawidget for different scenarios. For example:

public class PenguinBean {

   public UIMetawidget getMetawidget() {

      // First-time init
      // JSF spec: "When a component instance is first created (typically by virtue of being
      // referenced by a UIComponentELTag in a JSP page), the JSF implementation will retrieve the
      // ValueExpression for the name binding, and call getValue() on it. If this call returns a
      // non-null UIComponent value (because the JavaBean programmatically instantiated and
      // configured a component already), that instance will be added to the component tree that
      // is being created"

      UIMetawidget metawidget = new HtmlMetawidget();
      initMetawidget( metawidget );
      return metawidget;

   public void setMetawidget( UIMetawidget metawidget ) {

      // POST-back init
      // JSF spec: "When a component tree is recreated during the Restore View phase of
      // the request processing lifecycle, for each component that has a ValueExpression
      // associated with the name 'binding', setValue() will be called on it, passing the
      // recreated component instance"

      initMetawidget( metawidget );
   private void initMetawidget( UIMetawidget metawidget ) {
      ...configure Metawidget programmatically...

This (little used) feature of JSF is a great way to 'open up' the programmatic API of Metawidget inside your JSF applications.

Feedback welcome!