I am trying to build a navigation tree via recursion in JSF. I have defined a navigationNode component as:
JSF's built-in declarative tags are ill-suited for handling this sort of recursion. JSF builds a stateful component tree that is persisted between requests. If the view is restored in a subsequent request, the view state may not reflect changes in the model.
I would favour an imperative approach. You have two options as I see it:
binding attribute to bind a control (e.g. some form of panel) to a backing bean that provides the UIComponent instance and its children - you write code to instantiate the UIComponent and add whatever children you want. See the spec for the binding attribute contract.UIComponent; a Renderer; a tag handler; meta-data files (delete as appropriate - you do some or all of these depending on what you are doing and how and in which version of JSF).Perhaps another option is to pick up a 3rd party control that already does this.
UPDATE:
If one is using the very useful OmniFaces library (you should if you don't already), there is the
-
#{node.index} #{item.someProperty}
EDIT:
Here's a model-driven approach that doesn't involve writing custom components or backing-bean-generated component trees. It's kind of ugly.
The Facelets view:
Facelet Tree
-
The managed bean:
@javax.faces.bean.ManagedBean(name = "tree")
@javax.faces.bean.RequestScoped
public class Tree {
private Node root = new Node(null, "JSF Stuff");
@PostConstruct
public void initData() {
root.getKids().add(new Node(root, "Chapter One"));
root.getKids().add(new Node(root, "Chapter Two"));
root.getKids().add(new Node(root, "Chapter Three"));
Node chapter2 = root.getKids().get(1);
chapter2.getKids().add(new Node(chapter2, "Section A"));
chapter2.getKids().add(new Node(chapter2, "Section B"));
}
public List> getTreeNodes() {
return walk(new ArrayList>(), root);
}
private List> walk(List> list, Node node) {
list.add(node);
for(Node kid : node.getKids()) {
walk(list, kid);
}
return list;
}
}
A tree node:
public class Node {
private T value;
private Node parent;
private LinkedList> kids = new LinkedList<>();
public Node(Node parent, T value) {
this.parent = parent;
this.value = value;
}
public List> getKids() {return kids;}
public T getValue() { return value; }
public boolean getHasParent() { return parent != null; }
public boolean isFirstChild() {
return parent != null && parent.kids.peekFirst() == this;
}
public boolean isLastChild() {
return parent != null && parent.kids.peekLast() == this;
}
public List getLastChildLineage() {
Node node = this;
List lineage = new ArrayList<>();
while(node.isLastChild()) {
lineage.add(node);
node = node.parent;
}
return lineage;
}
}
Output:
* JSF Stuff
o Chapter One
o Chapter Two
+ Section A
+ Section B
o Chapter Three
I would still bite the bullet and write a custom tree control.