Tree Table Row Expansion
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?
Row Object Representation
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>
Tree Data Representation
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.