我目前正在与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。
我理解为什么它不起作用,但我完全不知道如何实现它。有谁知道怎么做?
答案 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中的请求范围。