programmatic changes to HtmlForm do not get rendered
What I want to do sounds actually very simple:
I have a form containing a button which when clicked should dynamically add new components (input field, text) to the form. These components are added programmatically in the backing bean method which is called when the button gets clicked.
When checking the HtmlForm component in the debugger these objects properly appear in childrenList of the form object and they add up there when clicking the button multiple times.
The problem is, that the created components never appear on the page in the browser. I've been looking now for quite some time but didn't find any solution to this problem. It seems that the renderer just doesn't know that the component tree has changed. Do I have to notify the JSF framework/the renderer/whatever somehow to refresh the view?
Calling facesContext.renderResponse(); in addRow() doesn't make any difference.
What am I missing or doing wrong here? Any help is greatly appreciated. See code below...
Thanks.
Environment: Tomcat 5.5.23, MyFaces 1.1.5
the simplified JSP fragment:
<h:form id="dynamicFormId" binding="#{dynamicFormBean.dynamicForm}">
<h:commandButton value="Add Row" action="#{dynamicFormBean.addRow}" immediate="true"/>
</h:form>
the simplified backing bean implementation:
class DynamicFormBean{
private HtmlForm dynamicForm;
// the method called when the button to add components to the form is clicked
publicvoid addRow(){
HtmlOutputText outputText =new HtmlOutputText();
outputText.setId("someid" );// this is just for simplicity, the real code creates unique ids ...
outputText.setValue("some label" );
dynamicForm.getChildren().add( outputText );
}
}
[2332 byte] By [
joleona] at [2007-11-27 1:21:39]

# 1
Try these small changes:
public String addRow() {
HtmlOutputText outputText = new HtmlOutputText();
outputText.setId( "someid" ); // this is just for simplicity, the real code creates unique ids ...
outputText.setValue( "some label" );
dynamicForm.getChildren().add( outputText );
return "reload";
}
And in your faces-config.xml file, add a navigation rule for the JSP with the dynamic form:
<navigation-rule>
<from-view-id>/dynamicForm.jsp</from-view-id>
<navigation-case>
<from-outcome>reload</from-outcome>
<to-view-id>/dynamicForm.jsp</to-view-id>
</navigation-case>
</navigation-rule>
Notice that I added a String return on your action method. What this should do is force JSF to reload the view, and thus the component tree (when coupled with an appropriate navigation case).
CowKing
# 3
This shouldn't matter. In this specific case, adding components happens in the invoke_application phase which is before the render_response phase.
I recall some problems with HtmlForm in such situations, but I'm not sure anymore. I'll test it at home meanwhile.
Anyway try to use h:panelGroup to add dynamically components to it, this should work for sure:
JSF<h:form>
<h:panelGroup binding="#{myBean.group}" />
<h:commandButton value="add" action="#{myBean.add}" />
</h:form>
MyBeanprivate HtmlPanelGroup group; // + getter + setter
public void add() {
HtmlOutputText text = new HtmlOutputText();
// setting ID is not required.
text.setValue("foo");
group.getChildren().add(text);
}
Is MyFaces 1.1.5 based on JSF 1.1 or 1.2?
# 5
There was a bug (or just an "unfeature") in HtmlForm somewhere in JSF 1.1. I've tested both snippets in JSF 1.1_02 as well as JSF 1.2_04 and dynamically adding components to the HtmlForm only works in JSF 1.2. In JSF 1.1 they indeed won't be rendered somehow.
By the way, programatically adding an unique ID to the group turns out to be required in JSF 1.1, otherwise you will get the Duplicate ID error when adding the 2nd component. In JSF 1.2 you don't need to care about programatically adding unique ID's.
In JSF 1.1 you can do something like:public void add() {
HtmlOutputText text = new HtmlOutputText();
text.setId("text" + group.getChildCount());
text.setValue("foo");
group.getChildren().add(text);
}
Assuming the childCount would never decrease.
# 6
I've come across this before. I don't think it's a bug in the form component - it's the JSF 1.1 tree-building rendering bug that was fixed with JSP 2.1/JSF 1.2.
It happens because the form component does not render its children. If the children are added dynamically, there are no JSP tag classes to call the encode methods for the children. This means they simply never get rendered. Warning: I'm nowhere near being an expert in this stuff so this explanation may be very wrong :-)
The easiest way to work around this is to put the form inside a component that does render its children, like a panelGroup or panelGrid:
<h:panelGroup>
<h:form binding=".." />
</h:panelGroup>
That way the panelGroup will take responsibility for rendering every descendent component.
I've tried asking the JSF RI users mailing list about this but didn't get many replies and this is the only solution I came up with.