JSF Conditional includes, cause Component ID has already been found in the view
Asked Answered
A

1

24

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!

Alfonzoalford answered 12/9, 2013 at 15:33 Comment(5)
If both single.xhtml and double.xhtml files contains almost the exact structure, why not to have a single page instead?Oarsman
It's not the exact structure.. it's totally different, with some shared fields among them.. And I don't want to change the IDs of these shared fields :) @LuiggiMendozaAlfonzoalford
You could either wrap them in an invisible NamingContainer in your parent page or in the child pages. It's easy to make such an invisible naming container with a composite component.Uttermost
@Uttermost Please read the last section of my post :)Alfonzoalford
Yes, hence I commented instead of answering - I knew there was a better way and I see BalusC has pointed it out already :)Uttermost
Z
34

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:

Zayas answered 12/9, 2013 at 16:13 Comment(2)
Upgrading from 2.1.2 to 2.1.18 saved it for me.Harty
@Joerg: You're welcome. Do note that 2.1.x is currently already at 2.1.29.Zayas

© 2022 - 2024 — McMap. All rights reserved.