Hi,
Technical problem is the context of #{cc} and statefulness of #{cc.attrs}.
The #{cc} refers to *current* composite. So when you pass #{cc} to the
nested composite , it will in turn refer the nested composite itself, not
its parent. This results in infinite loop. This can be tricked by passing
#{cc.parent} to the nested composite instead.
The statefulness of #{cc.attrs} can be tricked by overriding
setValueExpression() and delegating to a local variable instead of
statehelper and then referring the local variable via #{cc.node} instead of
the attribute. This will avoid another infinite loop when nested component
refers attributes of its parent.
Here's an example:
<cc:interface componentType="treeComposite">
<cc:attribute name="node" type="org.omnifaces.model.tree.TreeModel" />
</cc:interface>
<cc:implementation>
<c:if test="#{not empty cc.node.children}">
<ul>
<c:forEach items="#{cc.node.children}" var="node" varStatus="loop">
<li>#{node.data} <my:tree node="#{cc.parent.node.children[loop.index]}"
/></li>
</c:forEach>
</ul>
</c:if>
</cc:implementation>
With this backing component:
@FacesComponent("treeComposite")
public class TreeComposite extends UINamingContainer {
private TreeModel node;
@Override
public void setValueExpression(String name, ValueExpression binding) {
if ("node".equals(name)) {
setNode((TreeModel)
binding.getValue(getFacesContext().getELContext()));
}
else {
super.setValueExpression(name, binding);
}
}
public TreeModel getNode() {
return node;
}
public void setNode(TreeModel node) {
this.node = node;
}
}
By the way, just a few remarks: this is a composite component, not a custom
component. The <cc:attribute class> doesn't exist, you meant to use
<cc:attribute type>. The <c:forEach> doesn't support rendered attribute.
Just omit it.
Cheers, B
On Mon, Feb 8, 2016, 17:43 Michael Müller <michael.mueller_at_mueller-bruehl.de>
wrote:
> Hi,
>
> I defined a node element with a text, an URL, and a list of nodes. Using
> JSF, this should be shown as a kind of menu or tree, which might be
> expanded or collapsed.
>
> I created a custom component "MenuNode" to handle the tree in a recursive
> way.
> Using ui:repeat, I allways get a stack overflow.
> Using c:forEach together with c:if, I'll get the stack overflow every time
> the parent node is expanded.
>
> <cc:interface>
> <cc:attribute name="parentNode"
> class="org.mm.portallib.tree.MenuTreeNode"/>
> </cc:interface>
>
> <!-- IMPLEMENTATION -->
> <cc:implementation>
> <c:forEach items="#{cc.attrs.parentNode.children}" var="node"
> varStatus="status" rendered="#{cc.attrs.parentNode.children.size() gt 0}">
> <div style="margin-left:16px;">
> <mm:MenuItem/>
> <c:if test="#{node.expanded}" id="SubNode#{status.index}">
> <mm:MenuNode parentNode="#{node}"/>
> </c:if>
> </div>
> </c:forEach>
>
> </cc:implementation>
>
> It seems, JSF will analyze this structure independent from its data
> content: The structure terminates after few levels, whilst the tree build
> seems to run into an endless loop. Is this a bug, or does cc do not support
> any recursion?
>
> --
>
> Herzliche Grüße - Best Regards,
>
> Michael Müller
> Brühl, Germany
> blog.mueller-bruehl.de
> it-rezension.de
> @muellermi
>
>
> Read my books
> "Web Development with Java and JSF": <https://leanpub.com/jsf>
> https://leanpub.com/jsf
> "Java Lambdas und (parallel) Streams" Deutsche Ausgabe:
> <https://leanpub.com/lambdas-de>https://leanpub.com/lambdas-de
> "Java Lambdas and (parallel) Streams" English edition:
> <https://leanpub.com/lambdas>https://leanpub.com/lambdas
>
>