0

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.

mabi
  • 5,279
  • 2
  • 43
  • 78

2 Answers2

0

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).

Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
  • Thanks, I totally missed View-scope. But I've to revert my "works all fine" statement. When I save the Exercise, all elements are created in the database but come up empty (i.e. in the state that is shown in the frontend). Do you know how this could be? I can see in Firebug that the control sends it's content to the server but it doesn't seem to apply these values to the model? Finally, I'd love to switch to JSF2 but this app is bound to be replaced with something more modern soon, anyway. – mabi Jul 11 '12 at 08:06
  • What solution did you use? Because I have a page with a datatable with inputText and calendar input components, my bean has the `@KeepAlive` annotation and it works flawlessly. – Luiggi Mendoza Jul 11 '12 at 14:19
  • No solution yet, been busy with other bugs. Do your inputTexts refer to a CollectionOfElements? I fear JSF might not be able to update the List backing the inputTexts, but I can't prove that, yet. I'll setup a regular Entity with just the string in it (and a @ManyToOne back to `M`) and try again. – mabi Jul 11 '12 at 20:02
  • I don't use Hibernate instead DTOs to handle the model data. Still, I have a `List` inside my managed bean, my managed bean has the `@KeepAlive(ajaxOnly=false)` annotation (just like the `@Entity` annotation), and the List is updated with the entire model every time the user clicks a `` or `` (it works with h too). In any case, try to use the ``. – Luiggi Mendoza Jul 11 '12 at 20:07
  • Hm, I can try with `ajaxOnly=false` tomorrow, but I'd think it's the default. The `a:` taglib is actually `a4j`, sorry for that confusion. – mabi Jul 11 '12 at 20:14
  • I've tried and @KeepAlive sadly didn't do it for me. I'd actually very much prefer your way but see my answer for what I've ended up with. If you can improve on that, that'd be very much welcome because abusing my model like this just isn't right. – mabi Jul 12 '12 at 07:50
0

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; }
}
mabi
  • 5,279
  • 2
  • 43
  • 78