I hope that I understood JSF correct and this all makes sense. I try to do some kind of simple templating within a page by using (conditional) includes.
The panel is updated by a selection.
<p:outputPanel id="panel">
<h:panelGroup rendered="#{not empty someBean.selectedObject}">
<ui:include src="WEB-INF/pages/#{someBean.selectedObject.pageName}.xhtml" />
</h:panelGroup>
</p:outputPanel>
If I am right the ui:include got processed in some kind of view preparation phase and the rendered attribute just before the page gets rendered. As a result I get a FileNotFoundException because it tries to load WEB-INF/pages/.xhtml. This makes quite some sense to me, but how to solve this problem without a messy hackaround like creating an empty page as a prefix for the filename (page.xhtml) and prefix every page that should be actually rendered with this string (pageSamplePage.xhtml)?
You need to conditionally build the <ui:include> instead of conditionally render it. Use <c:if> instead of rendered.
<p:outputPanel id="panel">
<c:if test="#{not empty someBean.selectedObject}">
<ui:include src="WEB-INF/pages/#{someBean.selectedObject.pageName}.xhtml" />
</c:if>
</p:outputPanel>
Otherwise, the <ui:include> still ends up in the component tree.
See also:
JSTL in JSF2 Facelets... makes sense?
Unrelated to the concrete problem, even when you intend to conditionally render parts of the view, you'd better use <ui:fragment> instead of <h:panelGroup> as it has less overhead.
Prevent usage of <c:if>, as it can break some of the standard JSF components.
We observed that its usage caused duplication of a child within UI:Panel as a side-effect, and it took a while to identify this as the root-cause.
Related
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:
JSTL in JSF2 Facelets... makes sense?
I am trying to conditionally build a custom list using <ui:repeat>. On every occurrence of -1 as item-value in list, I need to add a line break.
I tried to use <c:if> inside <ui:repeat> for that, but it does not seem to work. It always evaluates false.
<ul>
<ui:repeat value="#{topics.list}" var="topicId" >
<li>#{topicId}</li>
<c:if test="#{topicId eq -1}"> <br/> </c:if>
</ui:repeat>
</ul>
Is this possible?
Not with JSTL tags, no. They run during view build time, not during view render time. You can visualize it as follows: when JSF builds the view, JSTL tags run from top to bottom first and the result is a pure JSF component tree. Then when JSF renders the view, JSF components run from top to bottom and the result is a bunch of HTML. So, JSTL and JSF don't run in sync as you'd expect from the coding. At the moment your <c:if> JSTL tag tag runs, the #{topicId} variable which is set by <ui:repeat> JSF component isn't available in the scope.
Instead of using <c:if>, you need to specify the condition in the rendered attribute of the JSF component of interest. As you've actually none, you could wrap it in a <ui:fragment>.
<ul>
<ui:repeat value="#{topics.list}" var="topicId" >
<li>#{topicId}</li>
<ui:fragment rendered="#{topicId eq -1}"><br/></ui:fragment>
</ui:repeat>
</ul>
Alternatives are <h:panelGroup>
<h:panelGroup rendered="#{topicId eq -1}"><br/></h:panelGroup>
or in your specific case <h:outputText escape="false">
<h:outputText value="<br/>" escape="false" rendered="#{topicId eq -1}" />
as both also emits nothing else to the HTML output when no client side attributes are specified.
See also:
JSTL in JSF2 Facelets... makes sense?
Unrelated to the concrete problem, that's the wrong place for a <br/>. It would be ignored by any webbrowser respecting the HTML specification. Don't you mean it to be inside the <li>? Or better, give it a class and let CSS give it a margin-bottom.
I am trying to conditionally build a custom list using <ui:repeat>. On every occurrence of -1 as item-value in list, I need to add a line break.
I tried to use <c:if> inside <ui:repeat> for that, but it does not seem to work. It always evaluates false.
<ul>
<ui:repeat value="#{topics.list}" var="topicId" >
<li>#{topicId}</li>
<c:if test="#{topicId eq -1}"> <br/> </c:if>
</ui:repeat>
</ul>
Is this possible?
Not with JSTL tags, no. They run during view build time, not during view render time. You can visualize it as follows: when JSF builds the view, JSTL tags run from top to bottom first and the result is a pure JSF component tree. Then when JSF renders the view, JSF components run from top to bottom and the result is a bunch of HTML. So, JSTL and JSF don't run in sync as you'd expect from the coding. At the moment your <c:if> JSTL tag tag runs, the #{topicId} variable which is set by <ui:repeat> JSF component isn't available in the scope.
Instead of using <c:if>, you need to specify the condition in the rendered attribute of the JSF component of interest. As you've actually none, you could wrap it in a <ui:fragment>.
<ul>
<ui:repeat value="#{topics.list}" var="topicId" >
<li>#{topicId}</li>
<ui:fragment rendered="#{topicId eq -1}"><br/></ui:fragment>
</ui:repeat>
</ul>
Alternatives are <h:panelGroup>
<h:panelGroup rendered="#{topicId eq -1}"><br/></h:panelGroup>
or in your specific case <h:outputText escape="false">
<h:outputText value="<br/>" escape="false" rendered="#{topicId eq -1}" />
as both also emits nothing else to the HTML output when no client side attributes are specified.
See also:
JSTL in JSF2 Facelets... makes sense?
Unrelated to the concrete problem, that's the wrong place for a <br/>. It would be ignored by any webbrowser respecting the HTML specification. Don't you mean it to be inside the <li>? Or better, give it a class and let CSS give it a margin-bottom.
I'm trying something (JSF2) like this:
<p>#{projectPageBean.availableMethods}</p>
<c:choose>
<c:when test="${projectPageBean.availableMethods == true}">
<p>Abc</p>
</c:when>
<c:otherwise>
<p>Xyz</p>
</c:otherwise>
</c:choose>
But this doesn't seem to work, although the EL expression in the top paragraph changes from false to true, the next paragraph always shows Xyz?
I also tried to change the test to:
${projectPageBean.availableMethods}
But still the same problem!
First and foremost: JSTL tags runs during view build time, not during view render time.
Your concrete problem suggests that #{projectPageBean} is been set during view render time, such as would happen when definied as <ui:repeat var>, <h:dataTable var>, <p:tabView var>, etc. It's thus null during view build time.
In that case, you should not be using a view build time tag to conditionally render HTML. You should instead use a view render time component to conditionally render HTML. As first choice, use <ui:fragment>:
<p>#{projectPageBean.availableMethods}</p>
<ui:fragment rendered="#{projectPageBean.availableMethods}">
<p>Abc</p>
</ui:fragment>
<ui:fragment rendered="#{not projectPageBean.availableMethods}">
<p>Xyz</p>
</ui:fragment>
By the way, there's in Facelets no need to switch between #{} and ${}. In contrary to JSP, in Facelets the ${} behaves exactly the same as #{}. To avoid potential confusion and maintenance trouble, I recommend to stick to #{} all the time.
See also:
Conditional rendering of non-JSF components (plain vanilla HTML and template text)
I am trying to conditionally build a custom list using <ui:repeat>. On every occurrence of -1 as item-value in list, I need to add a line break.
I tried to use <c:if> inside <ui:repeat> for that, but it does not seem to work. It always evaluates false.
<ul>
<ui:repeat value="#{topics.list}" var="topicId" >
<li>#{topicId}</li>
<c:if test="#{topicId eq -1}"> <br/> </c:if>
</ui:repeat>
</ul>
Is this possible?
Not with JSTL tags, no. They run during view build time, not during view render time. You can visualize it as follows: when JSF builds the view, JSTL tags run from top to bottom first and the result is a pure JSF component tree. Then when JSF renders the view, JSF components run from top to bottom and the result is a bunch of HTML. So, JSTL and JSF don't run in sync as you'd expect from the coding. At the moment your <c:if> JSTL tag tag runs, the #{topicId} variable which is set by <ui:repeat> JSF component isn't available in the scope.
Instead of using <c:if>, you need to specify the condition in the rendered attribute of the JSF component of interest. As you've actually none, you could wrap it in a <ui:fragment>.
<ul>
<ui:repeat value="#{topics.list}" var="topicId" >
<li>#{topicId}</li>
<ui:fragment rendered="#{topicId eq -1}"><br/></ui:fragment>
</ui:repeat>
</ul>
Alternatives are <h:panelGroup>
<h:panelGroup rendered="#{topicId eq -1}"><br/></h:panelGroup>
or in your specific case <h:outputText escape="false">
<h:outputText value="<br/>" escape="false" rendered="#{topicId eq -1}" />
as both also emits nothing else to the HTML output when no client side attributes are specified.
See also:
JSTL in JSF2 Facelets... makes sense?
Unrelated to the concrete problem, that's the wrong place for a <br/>. It would be ignored by any webbrowser respecting the HTML specification. Don't you mean it to be inside the <li>? Or better, give it a class and let CSS give it a margin-bottom.