4

我目前正在与 JSF 进行一些斗争。我想显示一个项目列表。每个项目可以显示 2 个 facelets(如果项目可编辑,则一个,否则一个)。

代码片段:

<div>
   <c:forEach items="#{bean.itemList}" var="item">
      <c:choose>
         <c:when test="#{bean.isEditable(item.id)}">
            <ui:include src="#{item.editableFaceletPath}>
               <ui:param name="item" value="#{item}" />
            </ui:include>
         </c:when>
         <c:otherwise>
            <ui:include src="#{item.normalFaceletPath}>
               <ui:param name="item" value="#{item}" />
            </ui:include>      
         </c:otherwise>
      </c:choose>
   </c:forEach>
</div>

只要我不将项目设置为可编辑,这就可以正常工作。但是,如果我有 3 个项目:item1、item2 和 item3,并且我将 item1 设置为可编辑,我将显示 item2、item2、item3。

我理解为什么它不起作用,但我完全不知道如何以其他方式实现它。有人知道怎么做吗?

4

2 回答 2

2

有关JSF 不同生命周期阶段评估的常见错误的说明,请参阅此链接。

问题是您的 JSTL 标记只会在构建视图时评估一次。如果您将项目更改为可编辑,它将不再对之前已经构建的组件树产生影响。

解决方案是用和替换<c:choose><c:when><c:otherwise>两个<ui:fragment>s 。rendered="#{bean.isEditable(item.id)}"rendered="#{not bean.isEditable(item.id)}"

这样,您的视图中将拥有组件树的两个分支,但在渲染时,由于该rendered属性,只有其中一个会被评估和显示。

但是,只要您不更改项目列表,整个结构就可以工作。因为添加或删除项目将不再影响<c:forEach>。在这种情况下,您必须完全不<ui:include>使用<ui:repeat>并使用<ui:fragment rendered="#{...}">.

于 2013-10-09T18:27:59.693 回答
2

如果您通过回发操作更改 JSTL 所依赖的模型,那么您需要告诉 JSF 重建视图,以便在呈现视图之前重新执行 JSTL。JSTL 标记是设计使然,即在视图渲染期间不会在新条件下重新执行。

public void someActionMethodWhichSetsItemEditable() {
    // Do actual job here.
    item.setEditable(true);

    // Then rebuild the view (re-executes all JSTL).
    FacesContext context = FacesContext.getCurrentInstance();
    String viewId = context.getViewRoot().getViewId();
    context.setViewRoot(context.getApplication().getViewHandler()
        .createView(context, context.getViewRoot().getViewId()));
}

当心:所有视图范围的 bean 都以这种方式被垃圾和重建。因此,如果您打算在请求中保持一些数据处于活动状态并将它们转换为请求范围的 bean 不是一种选择,那么让视图范围的 bean 在重建视图之前将数据放入请求范围并让它从构造后的请求范围。

于 2013-10-09T19:04:33.190 回答