Sunday, January 11, 2009

Dynamically generated Trinidad Component Trees and Input Values

I recently generated JSF pages dynamically with components from Apache Trinidad. The results are wonderful - and saved a great deal of iterative development time by allowing business analysts to specify the UI for various business scenarios. (For those interested I explain the basics of doing this later in this blog.)

We of course find a few issues:

1. When Using PPR, values do not always match bindings.
2. Components sometimes disappear!

Some of the components used PPR on the front side to update other components. I ran into a few issues while validating these fields - the values did not match the values shown in the UI. I scratched my head and rolled up the sleeves.

The issue is that JSF has 2 notions of values for an input component:

1. The component binding.
2. The component value.

The component value is typically the value of the binding. However, if you perform an XmlHttpRequest (PPR), these values may be out of sync. The scenario I used was that I generate the components and specify bindings to backing beans. This works fine until a PPR request is made and you update the model. The UI value becomes out of sync with the backing model. There are a few solutions to this issue:

1. Call comp.setValue(the value) on the actual component. This will require accessing the component directly.
2. Call comp.resetValue() - this syncs the value to the binding but also requires that you access the component on the server.
3. Use PartialTriggers when generating the component. This requires that you specify the id of the components that will cause a refresh of the value, and that you keep track of these ids. Then you specify the partial triggers on the component at the time of component generation.

I found that solution 3 works best for me, as I found another issue with generating components server side and using AJAX on the front. Sometimes, inexplicably, components would disappear. The reason for this is that Trinidad generates ids for components, and when you have components generated on the fly, there seems to be competition among the components for the ids. This seems to be a result of mixing generated and dynamically generated ids. The solution for this is to specify all ids for your generated components. Coupled with the task of specifying partialTriggers and using ids for javascript calls, this is the best comprehensive and cohesive approach.

A little example of a dynamically generated UI :


In your UI page you simply add a container with a binding to a managed bean. In the following line, I specify a panelAccordion (without opening and closing brackets) :
tr:panelAccordion binding="#{myManagedBean.accordionX}"

then in your backing bean you generate the PanelAccordion:

CorePanelAccordion accordionX = new PanelAccordion();

Make sure that you provide public access to this component. You then build the component by adding boxes, tables, and inputs as you need. You also set the various attributes on all the components.

For instance, lets add a Detail Item with a box and an input with a button:

FacesContext fc = FacesContext.currentInstance();
CoreShowDetailItem tab1 = new CoreShowDetailItem();
// the following names are the handles you would call in the ui.
String tab1Binding = "myManagerKeyString.tab1Name";
tab1.setValueBinding("binding" , fc.getApplication().createValueBinding("#{" + tab1Binding + "}"));

tab1.setDisclosed(true);



CorePanelBox pb1 = new CorePanelBox();
pb1.setStyleClass("yourStyleClass");



CoreInputText comp = new CoreInputText();
comp.setId(genYourId());
comp.setLabel("Label for Component");
comp.setConverter(new YourCompConverter());

// This is the value binding to the backing pojo
String compBindString = "myManagerKeyString.pojo.compAttributeRelationName";
comp.setValueBinding("value", fc.getApplication().createValueBinding(#{"+ compBindString + "}"));


CoreCommandButton button = new CoreCommandButton();
button.setText("GO");
addButton.setId(genYourId());

// create a methodBinding
String actionBindingString = "myMangerKeyString.go";
MethodBinding mb = fc.getApplication().createMethodBinding("#{" + actionBindingString + "}"));
button.setAction(mb);


Now lets add the components:


pb1.getChildren().add(comp);
pb1.getChildren().add(button);
tab1.getChildren().add(pb1);
accordionX.getChildren().add(tab1);

Then when the UI is displayed, it references accordionX, and in the backing bean you generate this UI. Your managed bean needs to provide access to the actions and the pojo. This was a simple case and you can of course enhance you components by specifying any more attributes.

A couple pieces of advice:

1. Specify styleClasses and use css to control layout.
2. ValueBindings can be used to specify all the attributes that you normally specify client side. For instance, if the style of a component is bound to a server side call:

String styleBinding= "myMangerKeyString.someStyleBindingCallName";
comp.setValueBinding("styleClass", fc.getApplication().createValueBinding("#" + styleBinding + "}" ));

You can use this to bind any attribute to a server call.


In a recent application, I used xml to specify the fields and their attributes possible in a UI. I then allowed business analysts to choose which components displayed when, and even some of the attributes such as label and order. Then, when the call is made to display a container, I generate the UI based on their selections. It worked quite well.
























No comments: