Reputation: 183
I have a pojo list within a pojo list that I am trying to use in a dynamic way in a data table with jsf 2.2.6 and primefaces 4.0. I think I am close but I haven't been able to make it work.
The idea is to configure multiple rounds of a golf competition. Main list (registrations) is all players with info like name, handicap factor, etc. The List within the registration list is tee times, start hole, etc per round where it could be anywhere from 1-4 rounds and I only want to display one at a time in columns of the p:dataTable. The user would click the round they want to see, using the selectOneButton and it would dynamically change to that data.
The key ideas are (1) selectOneButton and ajax to trigger the change of a property identifying the index to the table. (2) JSTL to help make the EL expressions shorter and more readable (3) Typical datatable to walk through the primary list (4) EL putting it all together to try and extract the right round based on the index chosen by the button. This is a stripped down version to show the problem but I don't think I have left out anything important. Here is the xhtml:
<c:set value="#{events.activeRoundIndex}" var="i"/>
<p:commandButton value="Update" action="#{events.regUpdateAll()}"
process="@form" update="@form" />
<div style="float: right">
<p:selectOneButton rendered="#{events.selectedEvent.noOfRounds gt 0}" value="#{events.activeRoundIndex}">
<f:selectItem itemLabel="1" itemValue="0"/>
<f:selectItem itemDisabled="#{events.selectedEvent.noOfRounds lt 2}" itemLabel="2" itemValue="1"/>
<f:selectItem itemDisabled="#{events.selectedEvent.noOfRounds lt 3}" itemLabel="3" itemValue="2"/>
<f:selectItem itemDisabled="#{events.selectedEvent.noOfRounds lt 4}" itemLabel="4" itemValue="3"/>
<f:ajax execute="@form" render="@form"/>
</p:selectOneButton>
</div>
<p:dataTable value="#{events.registrationList}" var="item" rowIndexVar="rowIndex">
<p:columnGroup type="header">
<p:row>
<p:column headerText="First Name" rowspan="2"/>
<p:column headerText="Last Name" rowspan="2"/>
<p:column headerText="Round #{i+1}" colspan="2"/>
</p:row>
<p:row>
<p:column headerText="Tee Time"/>
<p:column headerText="Start Hole"/>
</p:row>
</p:columnGroup>
<p:column>#{item.playerId.firstName}</p:column>
<p:column>#{item.playerId.lastName}</p:column>
<p:column>
<p:inputText value="#{item.roundList.get(i).teeTime}">
<f:convertDateTime type="time" timeStyle="short"/></p:inputText>
</p:column>
<p:column>
<p:inputText value="#{item.roundList.get(i).startingHole}"/>
</p:column>
</p:dataTable>
There are no crashes and the data is formatted correctly, just not retrieved from the correct location
Any direction would be greatly appreciated
BTW this question was re-written as I was unable to figure out the formatting to put smaller code snippets.
UPDATE: have proven the concept in another area of my code and diagnosed the problem further. The Update Model Values phase (4) is updating the wrong index in my list. i.e. if I am viewing hole 1 (index 0) and click for hole 2 it actually updates properties at index 1 rather than 0 in JSF phase 4 (Mojara). I think this could only occur due to the code shown above. Can anyone see what I am doing wrong or is it a system bug?
Further update with a complete and simple code example demonstrating the problem. By making P1 editable and P2 display you can see that the property is changed without user input. I believe it is a system bug but hope I am wrong as I really need this to work. Here is the code:
<p:panelGrid>
<p:row>
<p:column>Name</p:column>
<p:column>Selected Index</p:column>
<p:column>P1</p:column>
<p:column>P2</p:column>
</p:row>
<c:forEach items="#{test.list}" var="item2">
<c:set value="#{test.getMinor(item2)}" var="sub"/>
<p:row>
<p:column>#{item2.name}</p:column>
<p:column>#{test.selectedIndex}</p:column>
<p:column><p:inputText value="#{sub.p1}"/></p:column>
<p:column>#{sub.p2}</p:column>
</p:row>
</c:forEach>
</p:panelGrid>
</h:form>
and the SessionScoped JAVA code
public class Test {
private List<ItemHolder> list;
private int selectedIndex=0;
public Test() {
list = new ArrayList();
list.add(new ItemHolder());
}
public void doNothingAjax() {
}
public Item getMinor(ItemHolder holder) {
return holder.getItems().get(selectedIndex);
}
//Getters & Setters
public class ItemHolder {
private String name;
List<Item> items;
public ItemHolder() {
name = "Test";
items = new ArrayList();
items.add(new Item("index 0",0,0));
items.add(new Item("index 1",1,1));
}
//getters and setters
public class Item {
private String label;
private int p1; //property 1
private int p2; //property 2
public Item(String label, int p1, int p2) {
this.label = label;
this.p1 = p1;
this.p2 = p2;
}
//Getters and Setters
Any help would be greatly appreciated. If it is a system bug any suggestions for a workaround would also be helpful. I'm running out of ideas.
Thanks, John
Upvotes: 2
Views: 5113
Reputation: 634
The c:forEach
and c:set
are causing your problems. See various articles on the subject. (also see BalusC's comment)
Basically, the c:
tags do not save state (not part of the jsf component tree) and are causing what you noted:
The Update Model Values phase (4) is updating the wrong index in my list.
I didn't check, but I bet phase 4 is always updating the last available ('minor') sub.p1
. This is because by the time phase 4 runs, sub
(last 'minor') is a constant because it's state during each c:forEach
iteration is not saved in the component tree.
Suggestions?
c:
tags.c:forEach
use ui:repeat
c:set
, use the full path #{item2.items.get(test.selectedIndex).p1}
. (maybe a restructuring of the underlying data structures is needed)Upvotes: 2