This tutorial demonstrates using the ace:dataTable and its row expansion feature to represent a multi-level tree table with different data types for each level of the tree.
The hurdles with this representation that we need to overcome are questions of data representation. How do I represent a tree model? How do I represent a row of different types at each level?
To support each type you intend for the different levels of the table, each of the types must be amalgamated into a common type to design the set of ace:column components around.
In the example, the row common type is called PersonnelRowObject, intended for a tree table representing in a given row either a Person, a City, a Region or an Organization.
public class PersonnelRowObject {
Person person;
City city;
Region region;
Organization organization;
public PersonnelRowObject(Person person) {
this.person = person;
}
public PersonnelRowObject(City city) {
this.city = city;
}
public PersonnelRowObject(Region region) {
this.region = region;
}
public PersonnelRowObject(Organization organization) {
this.organization = organization;
}
public Person getPerson() {...}
public void setPerson(Person person) {...}
public City getCity() {...}
public void setCity(City city) {...}
public Region getRegion() {...}
public void setRegion(Region region) {...}
public Organization getOrganization() {...}
public void setOrganization(Organization organization) {...}
}
The set of ace:column components reference the fields of the PersonnelRowObject:
<ace:dataTable var="row" value="#{backing.data}">
<ace:column>
<ace:expansionToggler />
</ace:column>
<ace:column>
#{row.organization.name}
</ace:column>
<ace:column>
#{row.region.name}
</ace:column>
<ace:column>
#{row.city.name}
</ace:column>
<ace:column>
#{row.person.name}
</ace:column>
<ace:column>
#{row.person.phone}
</ace:column>
<ace:column>
<h:outputText value="#{row.person.born}">
<f:convertDateTime dateStyle="short" type="date" />
</h:outputText>
</ace:column>
<ace:rowExpansion />
</ace:dataTable>
With that representation of a row appropriate for each level of our tree, the final step is to create the POJO tree structure of our data with our new shared type. The required structure is a list of key->value entries, where the keys are the row objects for a given level, and the values are a list of any children entries that row might have. The following is a sample from the tutorial showing the initialization of the tree model from sample Organization objects which have child Regions objects which have child City objects, etc:
List<Map.Entry> data = new ArrayList<Map.Entry>() {{
for (Organization org : orgs) {
List<Map.Entry> orgChildren = new ArrayList<Map.Entry>(org.getRegionList().size());
for (Region region : org.getRegionList()) {
List<Map.Entry> regionChildren = new ArrayList<Map.Entry>(region.getCityList().size());
for (City city : region.getCityList()) {
List<Map.Entry> cityChildren = new ArrayList<Map.Entry>(city.getPersonList().size());
for (Person person : city.getPersonList())
cityChildren.add(new AbstractMap.SimpleEntry(
new PersonnelRowObject(person), null));
regionChildren.add(new AbstractMap.SimpleEntry(
new PersonnelRowObject(city), cityChildren)
);
}
orgChildren.add(new AbstractMap.SimpleEntry(
new PersonnelRowObject(region), regionChildren)
);
}
add(new AbstractMap.SimpleEntry(
new PersonnelRowObject(org), orgChildren)
);
}
}};
With the data properly structured, the only requirement for row expansion is to include a "expansionToggle" component in an ace:column and a rowExpansion component as a child of the table, no attributes necessary.