如何阻止递归复合组件递归包含自身

时间:2013-07-29 11:48:42

标签: jsf jsf-2 recursion facelets composite-component

是否有可能有一个具有ui:重复的JSF组件并且在重复调用内部使用相同的组件?这是因为我正在构建问题树:

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

目前我有stackoverflow?

2 个答案:

答案 0 :(得分:4)

使用视图构建时间标记来停止递归而不是视图渲染时间标记。组件树是在视图构建期间构建的。在视图构建期间不评估rendered属性,而是在视图渲染时间期间评估StackOverflowError属性。因此,您的构造基本上将自己包含在无限循环中。您应该已经知道<h:panelGroup rendered>是由递归引起的,因此内存堆栈无法再处理它(通常约为1000次迭代)。

<c:if test>替换为renderQuestions,它将按预期工作。通过省略对孩子的检查,可以简化<ui:repeat>。如果没有孩子,{{1}}无法呈现任何内容。

如果您碰巧在此构造中使用视图范围bean并且您正在使用Mojarra实现,那么请确保您升级到至少2.1.18,因为将视图构建时标记属性绑定到视图范围的bean属性将在旧版本中打破了视图范围。

另见:

答案 1 :(得分:0)

根据BalusC的答案,我创建了这样的解决方案。

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

在构建时使用level属性来避免无限递归,并且将在渲染时评估a4j:repeat(或ui:repeat)的renered属性。