users@javaserverfaces-spec-public.java.net

[jsr372-experts mirror] [jsr372-experts] Re: recursive use of a custom component

From: Bauke Scholtz <balusc_at_gmail.com>
Date: Mon, 8 Feb 2016 22:02:19 +0100

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
>
>