如何根据模型中的更改重新执行<c:choose> </c:choose>

时间:2013-10-09 15:08:20

标签: jsf jsf-2 jstl

我目前正在与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设置为editable,我将显示item2,item2,item3。

我理解为什么它不起作用,但我完全不知道如何实现它。有谁知道怎么做?

2 个答案:

答案 0 :(得分:2)

请参阅this link,了解有关JSF不同生命周期阶段评估的常见错误。

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

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

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

但只要您不更改项目的列表,整个构造就会起作用。因为添加或删除项目不会再影响<c:forEach>。在这种情况下,您必须在没有<ui:include>的情况下完全执行此操作,并使用<ui:repeat><ui:fragment rendered="#{...}">的组合。

答案 1 :(得分:2)

如果通过回发操作更改JSTL所依赖的模型,则需要告诉JSF重建视图,以便在呈现视图之前重新执行JSTL。 JSTL标记为by design,即在视图渲染时间内不会使用新条件重新执行。

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在重建视图之前将数据放入请求范围并让它读取数据来自postconstruct中的请求范围。