1

Is it possible to have a JSF component that has ui:repeat and inside the repeat call the same component? It's because I'm building tree of question:

<cc:interface>
    <cc:attribute name="questions" required="true" />
    <cc:attribute name="renderQuestions" required="true" default="true" />
</cc:interface>

<cc:implementation>

    <c:if test="#{cc.attrs.renderQuestions}">
        <ui:repeat value="#{cc.attrs.questions}" var="q">
            <p:panelGrid columns="2">
                <h:outputLabel value="#{q.question}"></h:outputLabel>
                <p:selectBooleanButton onLabel="#{messages['commons.yes']}"
                    offLabel="#{messages['commons.no']}" onIcon="ui-icon-check"
                    offIcon="ui-icon-close" value="#{q.ok}">
                    <p:ajax update="@all"></p:ajax>
                </p:selectBooleanButton>
            </p:panelGrid>

            <cf:question renderQuestions="#{q.ok}" questions="#{q.children}" />

        </ui:repeat>
    </c:if>

</cc:implementation>

Currently I'm having stackoverflow?

czetsuya
  • 4,773
  • 13
  • 53
  • 99
  • In theory there's not problem to nest two `ui:repeat` tags. However it depends on your `cf:question` tag's content. – Aritz Jul 29 '13 at 11:51
  • Hi, the content of the cf:question is the same code above, it's a recursive call that renders that view. The goal is to write the question and its child if it has. – czetsuya Jul 29 '13 at 11:52
  • Then, you [shouldn't rely in JSF declarative tags](http://stackoverflow.com/a/3497675/1199132) for doing that kind of recursion in view-side. Why don't you just build a double iteration over questions? – Aritz Jul 29 '13 at 11:57
  • Unfortunately what I want is an n-hierarchy of questions that's why double iteration won't do. – czetsuya Jul 29 '13 at 12:24

2 Answers2

4

Use a view build time tag to stop the recursion instead of a view render time tag. The component tree is built during view build time. The rendered attribute isn't evaluated during view build time, but instead during view render time. So your construct basically keeps including itself in an infinite loop. You should already know that StackOverflowError is caused by a recursion so deep that the memory stack can't handle it anymore (usually around ~1000 iterations).

Replace <h:panelGroup rendered> by a <c:if test> and it'll work as expected. The renderQuestions can by the way be simplified by omitting the check on children. The <ui:repeat> won't render anything anyway if there are no children.

If you happen to use view scoped beans in this construct and you're using Mojarra implementation, then make sure that you upgrade to at least 2.1.18, because binding a view build time tag attribute to a view scoped bean property would in older versions break the view scope.

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Hi Balus, thanks for the reply but after the changes you specified above I still have the same error. I'm updating the post above. – czetsuya Jul 29 '13 at 13:19
  • Apparently the expression didn't ever evaluate `false`. – BalusC Jul 29 '13 at 14:23
  • It seems like renderQuestions="#{q.ok}" doesn't work, but printing q.ok shows 'false' so it shouldn't render the view but it seems it does causing stackoverflow, weird. – czetsuya Jul 30 '13 at 02:52
0

Based on the answer of BalusC I created a solution like this.

<composite:interface>
    [..]
    <composite:attribute name="level" required="true" default="0"/>
</composite:interface>
<composite:implementation>

        <c:if test="#{cc.attrs.level == 0}">
            <a4j:repeat value="#{cc.attrs.list}" var="subList" rendered="#{cc.attrs.list != null}">

                    <xx:recursiveComponent list="#{subList}" id="subComponent" level="1"/>

            </a4j:repeat>   
        </c:if>

</composite:implementation>

The level attribute is used at build time to avoid endless recursion and the renered attribute of the a4j:repeat (or ui:repeat in your case) will be evaluated at render time.