Question

I've become stuck on this one:

    <rich:dataTable id="tabA" value="#{m.a}" rowKeyVar="nr" var="ex">
        <rich:column><h:outputText value="Exercise #{nr + 1}:" /></rich:column>
        <rich:column><h:inputText value="#{ex}" /></rich:column>
    </rich:dataTable>
    <h:panelGrid columns="2">
        <a4j:commandButton value="+" title="new" ajaxSingle="true"
            action="#{m.createNewExercise}" reRender="tabA"/>
        <a4j:commandButton value="-" title="remove last" ajaxSingle="true"
            action="#{m.deleteExercise}" reRender="tabA"/>
    </h:panelGrid>

Because this is part of a bigger form, I have set ajaxSingle="true".

The value I'm iterating over is a List of Strings, defined like this:

@Entity
public class M {
    ...
    private List<String> a = Lists.newArrayList();

    @CollectionOfElements
    public List<String> getA() { return a; }
    public void setA(List<String> a) { this.a = a; }
}

This fails to update the backing list a when submitted (submit is done via a simple <h:commandButton>). Since the content from the inputText elements is submitted, JSF seems failing to apply the values. Maybe someone can shed light on whether this is because of the @CollectionOfElements storage type in M.

So what I'm looking for is a way to save the ex values when calling createNewExercise or deleteExercise, either by not having to reRender the complete table or by sending them to the server first.

I can probably make it work via a binding like suggested here, but I'm interested in a way to avoid the overhead of a binding.

Was it helpful?

Solution 2

The problem is the ajaxSingle="true" attribute, combined with JSF's apparent inability to update a @CollectionOfElements.

The description of ajaxSingle clearly states that it skips all model updates not belonging to it's component (which is not the dataTable nor it's value attribute). So this is classic PEBKAC.

But this still doesn't work w/o ajaxSingle, which led me to believe the JSF side was OK when I was testing that option.

What I ended up with was:

<rich:dataTable id="tabA" value="#{m.a}" rowKeyVar="nr" var="ex">
    <rich:column><h:outputText value="Exercise #{nr + 1}:" /></rich:column>
    <rich:column><h:inputText value="#{ex}" /></rich:column>
</rich:dataTable>
<h:panelGrid columns="2">
    <a4j:commandButton value="+" title="new"
        action="#{m.createNewExercise}" reRender="tabA"/>
    <a4j:commandButton value="-" title="remove last"
        action="#{m.deleteExercise}" reRender="tabA"/>
</h:panelGrid>

And the Beans:

@Entity
public class M {
  ...
  private List<Exercise> a = Lists.newArrayList();

  @OneToMany(cascade = CascadeType.ALL)
  @JoinColumn(name = "ex_id")
  public List<Exercise> getA() { return a; }
  public void setA(List<Exercise> a) { this.a = a; }
}

@Entity
public class Exercise {
  [id definition omitted]
  private M m;
  private String description;

  @ManyToOne
  @JoinColumn(name = "ex_id", insertable = false, updatable =false)
  public M getM() { return m; }
  public void setM(M m) { this.m = m; }

  public String getDescription() { return description; }
  public void setDescription(String d) { this.description = d; }
}

OTHER TIPS

Easy way: Add the @KeepAlive annotation to your M bean. With this, your bean will be saveded and recovered for every request that you have on the same view, you won't need to worry about saving the bean data on session and recovery on (almost) every request.

Harder way: You must save the list value on session and recover it on every request made on your M bean.

Recommendation: If you could, move to JSF 2 and RF 4. In JSF 2 there is already a ViewScope that solves this kind of problems (and lot more).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top