Programmatically disclosing a node in af:tree and af:treeTable
- by Frank Nimphius
A common developer requirement when working with af:tree or af:treeTable components is to programmatically disclose (expand) a specific node in the tree.
If the node to disclose is not a top level node, like a location in a LocationsView -> DepartmentsView -> EmployeesView hierarchy, you need to also disclose the node's parent node hierarchy for application users to see the fully expanded tree node structure. Working on ADF Code Corner sample #101, I wrote the following code lines that show a generic option for disclosing a tree node starting from a handle to the node to disclose.
The use case in ADF Coder Corner sample #101 is a drag and drop operation from a table component to a tree to relocate employees to a new department. The tree node that receives the drop is a department node contained in a location. In theory the location could be part of a country and so on to indicate the depth the tree may have. Based on this structure, the code below provides a generic solution to parse the current node parent nodes and its child nodes.
The drop event provided a rowKey for the tree node that received the drop. Like in af:table, the tree row key is not of type oracle.jbo.domain.Key but an implementation of java.util.List that contains the row keys. The JUCtrlHierBinding class in the ADF Binding layer that represents the ADF tree binding at runtime provides a method named findNodeByKeyPath that allows you to get a handle to the JUCtrlHierNodeBinding instance that represents a tree node in the binding layer.
CollectionModel model = (CollectionModel) your_af_tree_reference.getValue();
JUCtrlHierBinding treeBinding = (JUCtrlHierBinding ) model.getWrappedData();
JUCtrlHierNodeBinding treeDropNode = treeBinding.findNodeByKeyPath(dropRowKey);
To disclose the tree node, you need to create a RowKeySet, which you do using the RowKeySetImpl class. Because the RowKeySet replaces any existing row key set in the tree, all other nodes are automatically closed.
RowKeySetImpl rksImpl = new RowKeySetImpl();
//the first key to add is the node that received the drop
//operation (departments).
rksImpl.add(dropRowKey);
Similar, from the tree binding, the root node can be obtained. The root node is the end of all parent node iteration and therefore important.
JUCtrlHierNodeBinding rootNode = treeBinding.getRootNodeBinding();
The following code obtains a reference to the hierarchy of parent nodes until the root node is found.
JUCtrlHierNodeBinding dropNodeParent = treeDropNode.getParent();
//walk up the tree to expand all parent nodes
while(dropNodeParent != null && dropNodeParent != rootNode){
//add the node's keyPath (remember its a List) to the row key set
rksImpl.add(dropNodeParent.getKeyPath());
dropNodeParent = dropNodeParent.getParent();
}
Next, you disclose the drop node immediate child nodes as otherwise all you see is the department node. Its not quite exactly "dinner for one", but the procedure is very similar to the one handling the parent node keys
ArrayList<JUCtrlHierNodeBinding> childList = (ArrayList<JUCtrlHierNodeBinding>) treeDropNode.getChildren();
for(JUCtrlHierNodeBinding nb : childList){
rksImpl.add(nb.getKeyPath());
}
Next, the row key set is defined as the disclosed row keys on the tree so when you refresh (PPR) the tree, the new disclosed state shows
tree.setDisclosedRowKeys(rksImpl);
AdfFacesContext.getCurrentInstance().addPartialTarget(tree.getParent());
The refresh in my use case is on the tree parent component (a layout container), which usually shows the best effect for refreshing the tree component.