It seems that the var defined as part of a dynamic accordion does not propagate to <c:forEach> (the references are always null inside the forEach). Strangely, <ui:repeat> works just fine. Now, I could avoid <c:forEach> as a workaround but it seems like a bit of a hack and a gotcha for the rest of my development team to trip over.
Here is some very simplistic code that demonstrates the problem:
Code: Select all
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:pe="http://primefaces.org/ui/extensions"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head>
<title>PrimeFaces Accordion/Input Bug</title>
</h:head>
<h:body>
<h:form>
<p:accordionPanel
value="#{tabBean.playerPositions.keySet().toArray()}"
var="player"
multiple="true"
activeIndex="#{tabBean.activeIndex}"
dynamic="true">
<p:tab title="#{player.name}">
<h:panelGrid>
<h:panelGrid columns="2">
<h:outputText value="Name: " />
<h:outputText value="#{player.name}"/>
<h:outputText value="Number: " />
<h:outputText value="#{player.number}"/>
<h:outputText value="Position: " />
<table>
<c:forEach var="position" items="#{tabBean.playerPositions[player]}">
<tr>
<td>
<h:selectBooleanCheckbox value="#{tabBean.positionSelections[position]}" >
<f:ajax event="click" execute="@form" render="@form" />
</h:selectBooleanCheckbox>
</td>
<td>
<h:outputText value="#{position.name}" />
</td>
</tr>
</c:forEach>
</table>
</h:panelGrid>
</h:panelGrid>
</p:tab>
</p:accordionPanel>
</h:form>
</h:body>
</html>
Code: Select all
@Named
@SessionScoped
public class TabBean implements Serializable {
private Map<Player, Set<Position>> playerPositions;
private Map<Position, Boolean> positionSelections;
public TabBean() {
playerPositions = new HashMap<Player, Set<Position>>();
positionSelections = new HashMap<Position, Boolean>();
Player player = new Player("Messi", 10);
Set positions = new HashSet<Position>();
Position position = new Position(player.getName(), "Forward");
positions.add(position);
positionSelections.put(position, Boolean.TRUE);
position = new Position(player.getName(), "Midfielder");
positions.add(position);
positionSelections.put(position, Boolean.FALSE);
playerPositions.put(player, positions);
player = new Player("Iniesta", 8);
positions = new HashSet<Position>();
position = new Position(player.getName(), "MidFielder");
positions.add(position);
positionSelections.put(position, Boolean.TRUE);
position = new Position(player.getName(), "Defender");
positions.add(position);
positionSelections.put(position, Boolean.TRUE);
position = new Position(player.getName(), "Sweeper");
positions.add(position);
positionSelections.put(position, Boolean.FALSE);
playerPositions.put(player, positions);
player = new Player("Villa", 7);
positions = new HashSet<Position>();
position = new Position(player.getName(), "Goalkeeper");
positions.add(position);
positionSelections.put(position, Boolean.TRUE);
position = new Position(player.getName(), "Captain");
positions.add(position);
positionSelections.put(position, Boolean.TRUE);
playerPositions.put(player, positions);
}
public Map<Player, Set<Position>> getPlayerPositions() {
return playerPositions;
}
public Map<Position, Boolean> getPositionSelections() {
return positionSelections;
}
public String getActiveIndex(){
return "1,2";
}
}
Code: Select all
public class Player implements Serializable {
private String name;
private int number;
public Player(String name, int number) {
this.name = name;
this.number = number;
}
public String getName() {
return name;
}
public int getNumber() {
return number;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof Player)) {
return false;
}
return ((Player) obj).getNumber() == this.number;
}
@Override
public int hashCode() {
int hash = 1;
return hash * 31 + name.hashCode();
}
@Override
public String toString() {
return name;
}
}
Code: Select all
public class Position implements Serializable {
private String player;
private String name;
public Position(String player, String name) {
this.player = player;
this.name = name;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Position other = (Position) obj;
if ((this.player == null) ? (other.player != null) : !this.player.equals(other.player)) {
return false;
}
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 5;
hash = 23 * hash + (this.player != null ? this.player.hashCode() : 0);
hash = 23 * hash + (this.name != null ? this.name.hashCode() : 0);
return hash;
}
@Override
public String toString() {
return name;
}
}
Thanks in advance. I have heard good things about PrimeFaces and this is my first time using it on a project.
PrimeFaces: 3.2
JSF: Mojarra/2.1.0-b11-FCS
Server: GlassFish/3.1.1