我知道我们不能重复同一视图树中任何组件的ID。
我有一个页面,其中包含某些条件下的其他页面像这样......
<h:panelGroup rendered="#{bean.insertMode == 'SINGLE'}">
<ui:include src="_single.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{bean.insertMode == 'DOUBLE'}">
<ui:include src="_double.xhtml" />
</h:panelGroup>
现在在这些页面中,我有“几乎”相同的组件层次结构(复杂)与不同的操作行为(不仅是方法调用,也是视图),例如:
_single.xhtml
<p:inputText id="fieldID" value="#{bean.value}" />
<p:commandLink actionListener="#{bean.singleAction()}" />
_double.xhtml
<p:inputText id="fieldID" value="#{bean.value}" />
<p:commandLink actionListener="#{bean.doubleAction()}" />
我的小例子工作正常,并按照预期渲染,但我得到了
java.lang.IllegalStateException: Component ID fieldID has already been found in the view.
我知道JSF会处理整个页面,即使它们没有被包含在内,这就是我得到这个例外的原因。
任何聪明的方法来解决这个问题而不改变包含页面内组件的ID(虽然它可以工作,但异常是烦人的,似乎有些不对劲)。
我不想用一些具有不同ID的容器组件来包装每个页面,因此它们将具有不同的FULL ID,如formId:fieldID,因为母版页也指这些组件里面包括这些!
答案 0 :(得分:33)
发生重复组件ID错误,因为两者都包含物理上最终在JSF组件树中。 <h:panelGroup rendered="false">
不会阻止它们进入JSF组件树,而是阻止它们生成HTML输出。
您需要在JSF组件树中有条件地构建它们,而不是有条件地呈现它们的HTML输出。 JSTL非常有用,因为它在视图构建期间运行:
<c:if test="#{bean.insertMode eq 'SINGLE'}">
<ui:include src="_single.xhtml" />
</c:if>
<c:if test="#{bean.insertMode eq 'DOUBLE'}">
<ui:include src="_double.xhtml" />
</c:if>
如果您正在使用Mojarra,您只需要确保至少使用2.1.18或更新版本,否则视图范围内的bean将表现得像请求范围的bean。
另一种方法是在src
属性中使用EL条件运算符(<ui:include>
本身在视图构建期间也作为标记处理程序运行):
<ui:include src="_#{bean.insertMode eq 'SINGLE' ? 'single' : 'double'}.xhtml" />
甚至可以直接使用insertMode
作为文件名:
<ui:include src="_#{fn:toLowerCase(bean.insertMode)}.xhtml" />
无论哪种方式,您都需要确保#{bean.insertMode}
在视图构建期间可用,并且在回发的恢复视图阶段期间也可以使用与初始渲染期间完全相同的值,否则可能会使用错误的include来恢复视图,并且JSF无法再解码正确的输入和命令。此外,当您想在回发期间更改包含时,您确实需要重建视图(返回非 - null
/ void
),或发送重定向。