views:

757

answers:

1

Hi, I'm having problems getting the data model of a HtmlDataTable to be correctly updated by JSF 2.0 and Facelets.

I have created a custom Java-based component that extends HtmlDataTable and dynamically adds columns in the encodeBegin method.

@Override
public void encodeBegin(FacesContext context) throws IOException
{
  if (this.findComponent("c0") == null)
  {
    for (int i = 0; i < 3; i++)
    {
      HtmlColumn myNewCol = new HtmlColumn();
      myNewCol.setId("c" + i);
      HtmlInputText myNewText = new HtmlInputText();
      myNewText.setId("t" + i);
      myNewText.setValue("#{row[" + i + "]}");
      myNewCol.getChildren().add(myNewText);
      this.getChildren().add(myNewCol);
    }
  }
  super.encodeBegin(context);
}

My test page contains the following

    <h:form id="fromtb">
      <test:MatrixTest id="tb" var="row" value="#{MyManagedBean.model}">
      </test:MatrixTest>
      <h:commandButton id="btn" value="Set" action="#{MyManagedBean.mergeInput}"/>
    </h:form>
    <h:outputText id="mergedInput" value="#{MyManagedBean.mergedInput}"/>

My managed bean class contains the following

@ManagedBean(name="MyManagedBean")
@SessionScoped
public class MyManagedBean 
{
private List model = null;
private String mergedInput = null;

public MyManagedBean() {
  model = new ArrayList();
  List myFirst = new ArrayList();
  myFirst.add("");
  myFirst.add("");
  myFirst.add("");
  model.add(myFirst);
  List mySecond = new ArrayList();
  mySecond.add("");
  mySecond.add("");
  mySecond.add("");
  model.add(mySecond);
}

public String mergeInput()
{
  StringBuffer myMergedInput = new StringBuffer();
  for (Object object : model)
  {
    myMergedInput.append(object);
  }
  setMergedInput(myMergedInput.toString());
  return null;
}

public List getModel() {
  return model;
}

public void setModel(List model) {
  this.model = model;
}

public String getMergedInput() {
  return mergedInput;
}

public void setMergedInput(String mergedInput) {
  this.mergedInput = mergedInput;
}

When invoked, the page is correctly rendered with a table made of 3 columns (added at runtime) and 2 rows (as my data model has 2 rows). However when the user enter some data in the input fields and then click the submit button, the model is not correctly updated and therefore the mergeInput() method creates a sequence of empty strings which is rendered on the same page.

I have added some logging to the decode() method of my custom component and I can see that the parameters entered by the user are being posted back with the request, however these parameters are not used to update the data model.

If I update the encodeBegin() method of my custom component as follow

@Override
public void encodeBegin(FacesContext context) throws IOException
{
  super.encodeBegin(context);
}

and I update the test page as follow

    <test:MatrixTest id="tb" var="row" value="#{MyManagedBean.model}">
      <h:column id="c0"><h:inputText id="t0" value="#{row[0]}"/></h:column>
      <h:column id="c1"><h:inputText id="t1" value="#{row[1]}"/></h:column>
      <h:column id="c2"><h:inputText id="t2" value="#{row[2]}"/></h:column>
    </test:MatrixTest>

the page is correctly rendered and this time when the user enters data and submits the form, the underlying data model is correctly updated and the mergeInput() method creates a sequence of strings with the user data.

Why does the test case with columns declared in the facelet page works correctly (ie the data model is correctly updated by JSF) where the same does not happen when the columns are created at runtime using the encodeBegin() method?

Is there any method I need to invoke or interface I need to extend in order to ensure the data model is correctly updated?

I am using this test case to address the issue that is appearing in a much more complex component, therefore I can't achieve the same functionality using a facelet composite component.

Please note that this has been done using NetBeans 6.8, JRE 1.6.0u18, GlassFish 3.0.

Thanks for your help.

+1  A: 

Your problem is probably here:

myNewText.setValue("#{row[" + i + "]}");

You're setting a string value here, not value binding. You'll need to do something like:

Application application = facesContext.getApplication();
ExpressionFactory expressionFactory = application
        .getExpressionFactory();
...
ValueExpression valueExpression = expressionFactory
            .createValueExpression(facesContext.getELContext(),
                    "#{row[" + i + "]}", String.class);
myNewText.setValueExpression("value", valueExpression );

Here's an a project which demonstrates programmatic usage of JSF 2.0.

lexicore
Hi, thanks for the suggestion, however using the setValueExpression instead of the setValue method did not solve the problem. On submission of the form the data model is not updated.Any other suggestion?
mikic
This is a complex setup, many things may go wrong. What I pointed out was in any case a problem. Try tracing/debugging, set a breakpoint in the decode methode of the input text control renderer and trace from there on.
lexicore