19 Aug 2012

Hand made dynamic form

There is some interesting component we can use in the UI whenever we need to get input controls rendered dynamically, on the fly.  This is dynamic:form. I really love it, because it's quiet unpretentious and stable. This feature can be extremely useful in use-cases where we have a user page, supposed to render a row of some dynamically created view object. The page is going to be universal one. It has no idea about what particular view object is providing the data. Any dynamically created view object can be accepted as a datasource. For example, in the PageDef file we have the following iterator definition:

<iterator Binds="#{someViewObjectName}" RangeSize="25" 
               DataControl="AgileDataModelServiceDataControl"
               id="VAgileIterator"/>


Notice, that we used dynamic binding approach. The EL #{someViewObjectName} is going to be evaluated as a name of some view object instance in the AgileDataModelService application module. It could be either dynamic VO or static VO, actually it doesn't matter. And we put on the page a dynamic form:

<dynamic:form value="#{bindings.VAgileIterator}" id="f1"/>

And that's it. It works! The result looks like this:


The magic is that we don't have any attribute bindings in the PageDef file. The dynamic form creates all necessary bindings itself at runtime. That's why I sad that dynamic:form is unpretentious component. It requires an iterator binding only.
So, the  dynamic:form component is perfect. But it has some limitations as well. The attributes within dynamic form are going to be rendered in a straight forward way using standard controls derived from the model layer. We can break through the limitations and build our custom, let’s say hand made, implementation of a dynamic form, just using “iterator + switcher” approach. In this case we have a full control of how attributes are being rendered and we are free to use our custom components. For example, a declarative component would be more suitable to represent some complex data structure. But the question is what should be provided as a model for these components? Since we represent the data from a dynamic view object with an arbitrary set of attributes, we don’t have any attribute bindings in the PageDef file. Of course, we could implement some model in a backing bean working with View Object Row directly, but that’s not feng shui. It’s preferable to work with business components model through the bindings layer.
The solution is to ask the framework for help.

We can define in the PageDef file a tree binding with an empty node definition inside it:

<tree IterBinding="VAgileIterator" id="VAgile">
     <nodeDefinition Name="VAgile0" DefName=""/>
</tree>


The trick is to define an empty node definition. It doesn't refer to any view object definition. In this case the framework is going to automatically at runtime investigate the view definition structure including all necessary attributes according to the view object instance specified in the iterator binding. So, at runtime, the framework will build JUCtrlHierNodeBindings for each row according to the attributes of the View Object specified in the iterator bindings.

We're going to use this feature and feed control value bindings, kindly prepared for us by the framework to the components within af:iterator. So, we have a construction like this one:

 <af:iterator value=“#{backingBean.controlList}" var="bind" id="dc_i1">
    <af:switcher id="dc_s1" facetName="#{bind.hints.AgileTypeID}" 
                 defaultFacet="Default">
       <f:facet name="DATE">
          <af:inputDate id="dc_id1"
             label="#{bind.hints.label}”                                 
             value="#{bind.inputValue}”                              
             required="#{bind.hints.mandatory}"
             columns="#{bind.hints.displayWidth}"
             shortDesc="#{bind.hints.tooltip}"/>
        </f:facet>

     …


And we have a method in a backing bean, returning the list of control value bindings (JUCtrlValueBindings):

 private List<JUCtrlValueBinding> getControlList() {
   //Find current bindning container
   DCBindingContainer bindingContainer = 
     (DCBindingContainer) BindingContext.getCurrent().getCurrentBindingsEntry();
   //Find VAgile tree binding   
   JUCtrlHierBinding treeBinding = 
     (JUCtrlHierBinding) bindingContainer.findCtrlBinding("VAgile");

   //Find node binding for the current row of VAgileIterator iterator
   DCIteratorBinding iterator =
      bindingContainer.findIteratorBinding("VAgileIterator");
   Row currentRow = iterator.getCurrentRow();
   JUCtrlHierNodeBinding rowBinding = 
   treeBinding.findNodeByKeyPath(Arrays.asList(new Key[]{currentRow.getKey()}));

   //Resolve it's binding container. 
   //The framework filled it up with attribute bindings
   DCBindingContainer agileContainer = 
     (DCBindingContainer)rowBinding.getBindings();  

   //And build the list
   List<JUCtrlValueBinding> controlList = new ArrayList();
   for (AttributeDef attr: iterator.getAttributeDefs()) {
     JUCtrlValueBinding ctrlBinding = 
       (JUCtrlValueBinding) agileContainer.findCtrlBinding(attr.getName());
     controlList.add(ctrlBinding); 
   }  
   return controlList;
 }

Have a look at the "bind" variable in the af:iterator definition. This is an instance of JUCtrlValueBinding and it can be used in EL expressions as usual #{bindings.} variable in the way we're used to.

That's it!