I am adding (UIInput) to the footer columns (UIColumn) of a datatable (UIData) created dynamically. The UIData is bound to a datatable tag in the jsp. In the datatable, I just have headers and footers with the header having the labels and footer having the corresponding value in editable textbox. When I change the value and submit the form using a commandButton and I try to access the UIInput value using .getValue() in the action method, I just get the old values and not the values updated in the page. I tried binding it to an attribute in the backing bean and checked the values being set in the setter. I notice that the old values are being set and the values I updated in the page do not reflect in the action method or setter. I tried using .getValue, .getLocalValue, .getSubmittedValue. None of these give me the new values. Any suggestions what I might be doing worng?
The actual code does several other processing, but below code should help in replicating the issue. In the below code, I expect the TestString to output the modified values from the page. But it just returns old values. Below is the jsp:
<%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<html>
<head>
</head>
<f:view>
<body>
<h:form styleClass="form" id="form1">
<h:commandButton value="Save" action="#{TestPageBackingBean.save}" styleClass="commandExButton"/>
<h:outputText styleClass="label" value="Header Table"/>
<h:dataTable binding="#{TestPageBackingBean.headerDataTable}"></h:dataTable>
</h:form>
</body>
</f:view>
</html>
Below is the faces config:
<managed-bean>
<managed-bean-name>TestPageBackingBean</managed-bean-name>
<managed-bean-class>test.jsf.TestPageBackingBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Below is the backing bean code:
package test.jsf;
import java.io.IOException;
import javax.faces.component.UIColumn;
import javax.faces.component.UIData;
import javax.faces.component.UIInput;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
public class TestPageBackingBean {
private UIData headerDataTable = new UIData();
public TestPageBackingBean() {
}
public UIData getHeaderDataTable()
{
return getHeaderTable(headerDataTable);
}
public UIData getHeaderTable(UIData dataTable)
{
for (int i=0;i<10;++i)
{
dataTable.getChildren().add(getColumn(i));
}
return dataTable;
}
private UIColumn getColumn(int i)
{
UIOutput outputLabelText = new UIOutput();
UIInput inputFieldText = new UIInput();
UIColumn column = new UIColumn();
outputLabelText.setValue("Label" + i);
inputFieldText.setValue("test input" + i);
column.setHeader(outputLabelText);
column.setFooter(inputFieldText);
return column;
}
public String save() throws IOException {
String TestString = "";
FacesContext ctx = FacesContext.getCurrentInstance();
if (!ctx.getResponseComplete()) {
for (int i=0; i<headerDataTable.getChildren().size();++i)
{
TestString = TestString + (String)((UIInput)((UIColumn) headerDataTable.getChildren().get(i)).getFooter()).getValue();
}
System.out.println(TestString);
}
return "save";
}
public void setHeaderDataTable(UIData headerDataTable) {
this.headerDataTable = headerDataTable;
}
}
I tried running your demo code code under MyFaces 1.2.3 on Tomcat and Mojarra 2.0.0 Beta on Glassfish, but was unable to reproduce the problem - the save() method printed the values I entered into the fields.
(To use MyFaces, I had to change new UIData() to new HtmlDataTable(), probably due to how they implement the table renderer, but that is a minor change.)
I will note a couple of things about the bean:
- the table getter will keep adding columns every time it is called - like on a page refresh with server-side state saving
- keeping a reference to a UIComponent in a session bean usually is not a good idea; you would be better off using request scope for component bindings
- session beans are supposed to implement Serializable (though I realize not everyone does this) and UIComponents cannot be serialized
- your component might end up in multiple views if the user opens the page twice - concurrency issues
- according to the spec: when JSF creates the view, it will use the component bound via the getter; but, when it restores the view (on submit), it will set the component via the setter, so keeping a reference is (at best) redundant
You might want to change the getter to something like this:
private UIData headerDataTable;
public UIData getHeaderDataTable() {
if (headerDataTable == null) {
headerDataTable = new UIData();
getHeaderTable(headerDataTable);
}
return headerDataTable;
}
I am not confident that these changes will fix your issue, though - if you are still having trouble, try again with more details - your JSF implementation, the version, and the value of the *javax.faces.STATE_SAVING_METHOD* parameter in web.xml (if any).
The issue is not fully resolved yet.
I use RSA 7, with IBM JSF - Base Faces Support 7.0 and Enhanced Faces Components 7.0 on WAS 6.0 The javax.faces.STATE_SAVING_METHOD was 'server' by default.
I tried changing STATE_SAVING_METHOD to 'client'. It did print the changed value in the output but in label4 instead of label0 which I modified. On the next submit the value moved from label4 to label8. Seemed inconsistent.
I managed to workaround by pulling the values from requestParameterMap. If there is a fix for the issue please do let me know. McDowell - thanks for your inputs.