如何停止递归复合组件以递归方式包含自身

Posted

技术标签:

【中文标题】如何停止递归复合组件以递归方式包含自身【英文标题】:How to stop recursive composite component from including itself recursively 【发布时间】:2013-07-29 04:40:30 【问题描述】:

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

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

目前我正在使用 ***?

【问题讨论】:

理论上嵌套两个ui:repeat标签是没有问题的。但是,这取决于您的 cf:question 标签的内容。 嗨,cf:question 的内容与上面的代码相同,它是呈现该视图的递归调用。目标是编写问题及其子项(如果有)。 那么,你 shouldn't rely in JSF declarative tags 在视图端进行了这种递归。您为什么不针对问题构建一个双重迭代? 不幸的是,我想要的是一个 n 层次的问题,这就是为什么双迭代行不通的原因。 【参考方案1】:

根据 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 属性将在渲染时进行评估。

【讨论】:

【参考方案2】:

使用视图构建时间标签而不是视图渲染时间标签来停止递归。组件树是在视图构建时构建的。 rendered 属性不会在视图构建期间评估,而是在视图渲染期间评估。因此,您的构造基本上将自身包含在无限循环中。您应该已经知道 ***Error 是由递归引起的,该递归太深以至于内存堆栈无法再处理它(通常大约 1000 次迭代)。

&lt;h:panelGroup rendered&gt; 替换为&lt;c:if test&gt;,它将按预期工作。 renderQuestions 可以通过省略对子项的检查来简化。如果没有孩子,&lt;ui:repeat&gt; 无论如何都不会渲染任何东西。

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

另见:

JSTL in JSF2 Facelets... makes sense? - 解释“查看构建时间”与“查看渲染时间”

【讨论】:

您好 Balus,感谢您的回复,但在您在上面指定的更改之后,我仍然有同样的错误。我正在更新上面的帖子。 显然表达式从未计算过false 似乎 renderQuestions="#q.ok" 不起作用,但打印 q.ok 显示“假”,因此它不应该渲染视图,但似乎确实会导致 *** ,奇怪。

以上是关于如何停止递归复合组件以递归方式包含自身的主要内容,如果未能解决你的问题,请参考以下文章

如何真正的理解递归?

Vuejs:动态递归组件(树状结构)

vue 递归组件

为啥递归联合不适用于 PostgreSQL 中的复合类型

js算法初窥04(算法模式01-递归)

递归和迭代