I am trying to build a navigation tree via recursion in JSF. I have defined a navigationNode component as:
<composite:interface>
<composite:attribute name="node" />
</composite:interface>
<composite:implementation>
<ul>
<ui:repeat value="#{navigationTreeBean.getChildrenForNode(cc.attrs.node)}" var="child">
<li><navigation:navigationNode node="#{child}" /></li>
</ui:repeat>
</ul>
</composite:implementation>
My tree is declared as:
rootNode = new DefaultMutableTreeNode(new NodeData("Dashboard", "dashboard.xhtml"), true);
DefaultMutableTreeNode configurationsNode = new DefaultMutableTreeNode(new NodeData("Configurations", "configurations.xhtml"), true);
rootNode.add(configurationsNode);
I call component by:
<nav:navigationNode node="#{rootNode}" />
The problem is, this results in StackOverflowError.
There are a few references to building recursion in JSF (for example, c:forEach vs ui:repeat in Facelets). The common problem seems to be mixing the build-time and render-time components/tags. In my case:
- My composite component is actually a tag, which is executed when the tree is built
- ui:repeat is an actual JSF component, which is evaluated when the tree is rendered
Is the child component navigation:navigationNode actually processed before the ui:repeat component? If so, what object is it using for #{child}? Is it null (doesn’t seem so)? Is the problem here that the child component is actually created without even caring about the ui:repeat and so each time a new child component is created even though it is not necessarily wanted?
The c:forEach vs ui:repeat in Facelets article has a separate section for this (recursion). The suggestion is to to use c:forEach instead. I tried this, however it is still giving me the same StackOverflowError, with different trace that I cannot make sense of.
I know that we can also build components by extending UIComponent, but that approach (writing html in Java code) seems ugly. I would rather use MVC style / templates. However, if there are no other ways, do I have to implement this sort of recursion as UIComponent instead?
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:
bindingattribute to bind a control (e.g. some form of panel) to a backing bean that provides theUIComponentinstance and its children – you write code to instantiate theUIComponentand add whatever children you want. See the spec for thebindingattribute contract.UIComponent; aRenderer; 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
<o:tree>which has no html generation whatsoever but was specifically designed to support usecases like this.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:
The managed bean:
A tree node:
Output:
I would still bite the bullet and write a custom tree control.