JSF 条件包含,导致组件 ID 已在视图中找到 [英] JSF Conditional includes, cause Component ID has already been found in the view

查看:21
本文介绍了JSF 条件包含,导致组件 ID 已在视图中找到的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道我们不能重复同一视图树中任何组件的 ID.

我有一个页面,其中包含特定条件下的另一个页面,就像这样...

<ui:include src="_single.xhtml"/></h:panelGroup><h:panelGroup 渲染=#{bean.insertMode == 'DOUBLE'}"><ui:include src="_double.xhtml"/></h:panelGroup>

现在在这些页面中,我有几乎"相同的组件层次结构(复杂)和 不同 动作行为(不仅是方法调用,还有视图),例如:

_single.xhtml

<p:commandLink actionListener="#{bean.singleAction()}"/>

_double.xhtml

<p:commandLink actionListener="#{bean.doubleAction()}"/>

我的小例子工作正常,并按预期呈现,但我得到

java.lang.IllegalStateException: 组件 ID fieldID 已在视图中找到.

我知道 JSF 会处理整个页面,即使它们没有被包含在内,这就是我收到此异常的原因.

在不更改包含页面内组件的 ID 的情况下解决此问题的任何智能方法(虽然它有效,但异常令人讨厌并且似乎有问题).

我也不想用一些具有不同 ID 的容器组件来包装每个页面,因此它们会有不同的完整 ID,如 formId:fieldID,因为母版页也引用这些组件这些里面包括!

解决方案

出现重复的组件 ID 错误是因为这两个包含在 JSF 组件树中物理结束.<h:panelGroup rendering="false"> 不会阻止它们出现在 JSF 组件树中,而是会阻止它们生成 HTML 输出.

您需要在 JSF 组件树中有条件地构建它们,而不是有条件地呈现它们的 HTML 输出.JSTL 在这方面非常有用,因为它在视图构建期间运行:

<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' ?单":双"}.xhtml"/>

或者甚至直接使用 insertMode 作为文件名:

<ui:include src="_#{fn:toLowerCase(bean.insertMode)}.xhtml"/>

无论哪种方式,您都需要绝对确保 #{bean.insertMode} 在视图构建期间可用,并且在回发的恢复视图阶段可用完全相同的值正如在初始渲染期间一样,否则视图可能会使用错误的包含进行恢复,并且 JSF 无法再解码正确的输入和命令.此外,当您想在回发期间更改包含时,您确实需要重建视图(返回非null/void),或发送重定向.>

另见:

I know we can't repeat the ID of any component we have in the same view tree.

I have a page which includes another pages by certain condition Like this...

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

Now In these pages I have "Almost" the same components hierarchy (Complex) with different actions behaviour (Not only method calls, also view), for example:

_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()}" />

My little example works fine, and renders as it supposed to, but I get

java.lang.IllegalStateException: Component ID fieldID has already been found in the view.

I know that JSF process the full pages even if they are not included and that's why I'm getting this exception.

Any smart way to solve this without changing the IDs of the components inside the include pages (Although it works, but the exception is annoying and seems something is wrong).

I don't want also to wrap each one of the pages with some container component with a different ID so they would have a different FULL ID like formId:fieldID because the master page is also referring to these components inside these includes!

解决方案

The duplicate component ID error occurs because the both includes physically end up in the JSF component tree. The <h:panelGroup rendered="false"> doesn't prevent them from ending up in JSF component tree, instead it prevents them from generating their HTML output.

Instead of conditionally rendering their HTML output, you need to conditionally build them in the JSF component tree. JSTL is very helpful in this as it runs during view build time:

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

In case you're using Mojarra, you only need to make sure you use at least version 2.1.18 or newer, otherwise view scoped beans will behave like request scoped beans.

An alternative is to make use of EL conditional operator in src attribute (the <ui:include> itself runs as being a taghandler also during view build time):

<ui:include src="_#{bean.insertMode eq 'SINGLE' ? 'single' : 'double'}.xhtml" />

Or even use the insertMode directly as filename:

<ui:include src="_#{fn:toLowerCase(bean.insertMode)}.xhtml" />

Either way, you need to make absolutely sure that the #{bean.insertMode} is available during view build time, and also that exactly the same value is available during the restore view phase of postbacks as it was during initial render, otherwise the view would possibly be restored with the wrong include and JSF can't decode the right inputs and command anymore. Also, when you want to change the include during postback, you really need to rebuild the view (return non-null/void), or to send a redirect.

See also:

这篇关于JSF 条件包含,导致组件 ID 已在视图中找到的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆