Reputation: 12651
I have got a problem with my JSF-rendering. A given condition in Expression Language will not be executed in the right way. E.g:
Example 1
<f:param name="cat" value="#{product.category.uri}" rendered="#{product.category.parent.uri == null}" />
<f:param name="cat" value="#{product.category.parent.uri}" rendered="#{product.category.parent.uri != null}" />
Example 2
<c:if test="#{product.category.parent.uri == null}">
<f:param name="cat" value="#{product.category.uri}" />
</c:if>
<c:if test="#{product.category.parent.uri != null}">
<f:param name="cat" value="#{product.category.parent.uri}" />
</c:if>
Problem
In both examples, both my parameters will be added to my surrounding h:outputLink. I am not sure what other code to add, so if you guys need anything else in order to help me, I'll be happy to provide it.
Thanks in advance.
Example 3 (on request)
<?xml version='1.0' encoding='UTF-8' ?>
<ui:composition template="./WEB-INF/templates/base.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<ui:define name="content">
<c:choose>
<c:when test="#{webshop.productlist.size() > 0}">
<div id="spacer">
<ui:repeat value="#{webshop.productlist}" var="product">
<div id="block">
<p>
<h:outputLink value="product.xhtml">
#{product.name}
<c:choose>
<c:when test="#{product.category.parent.uri == null}">
<f:param name="cat" value="#{product.category.uri}" rendered="" />
</c:when>
<c:otherwise>
<f:param name="cat" value="#{product.category.parent.uri}" />
</c:otherwise>
</c:choose>
<f:param name="product" value="#{product.uri}" />
</h:outputLink>
</p>
</div>
</ui:repeat>
</div>
</c:when>
<c:otherwise>
(...)
</c:otherwise>
</c:choose>
</ui:define>
</ui:composition>
I have cleaned up this example a bit, but the essence is there. I have replaced the first examples by a when/otherwise construction, whether my product.category.parent.uri is null or not, it will give me the first result in this case.
Upvotes: 2
Views: 20129
Reputation: 1109532
Your core problem is that you're completely confusing view build time tags and view render time tags.
The view build time is that moment when a XHTML file is to be converted to a JSF component tree as available by FacesContext#getViewRoot()
. The view render time is that moment when the JSF component tree is about to produce HTML code, as initiated by UIViewRoot#encodeAll()
.
All JSTL <c:xxx>
tags and all JSF <ui:xxx>
tags which do not have a rendered
attribute run during view build time. All JSF <ui:xxx>
tags which do have a rendered
attribute and all JSF <h:xxx>
tags run during view render time. So, they don't run in sync as you'd expect from the coding.
Coming back to your concrete problem, this is two-fold:
The <f:param>
does as being a tag handler not support the rendered
attribute at all.
The #{product}
is in your code definied by <ui:repeat var>
, which is a view render time tag, but yet you're trying to let JSTL <c:xxx>
view build time tags depend on that. This will of course not work. The #{product}
is null
during the view build time, simply because the <ui:repeat>
hasn't run at that moment.
Your concrete problem can only be solved by using the view build time tag <c:forEach>
instead of the view render time tag <ui:repeat>
to iterate over products.
<c:forEach items="#{webshop.productlist}" var="product">
Unrelated to the concrete problem, the follwing clumsy block
<c:choose>
<c:when test="#{product.category.parent.uri == null}">
<f:param name="cat" value="#{product.category.uri}" rendered="" />
</c:when>
<c:otherwise>
<f:param name="cat" value="#{product.category.parent.uri}" />
</c:otherwise>
</c:choose>
can be replaced by the following simpler approach with help of the conditional operator in EL:
<f:param name="cat" value="#{empty product.category.parent.uri ? product.category.uri : product.category.parent.uri}" />
This way you must be able to keep using the <ui:repeat>
.
Upvotes: 10
Reputation: 536
According to the documentation, there is no rendered
attribute available for the f:param
tag so it is being ignored.
See for JSF <2.x - http://docs.oracle.com/javaee/5/javaserverfaces/1.2/docs/tlddocs/f/param.html
See for JSF >2.0 - http://javaserverfaces.java.net/nonav/docs/2.0/pdldocs/facelets/f/param.html
Now that may suggest that example 2 should still work, so I might question whether or not product.category.parent.uri
is really null as opposed to empty (i.e. blank string). Have you tried to test for empty (checks for null and empty string)?
<c:if test="#{empty product.category.parent.uri}">
<f:param name="cat" value="#{product.category.uri}" />
</c:if>
<c:if test="#{!empty product.category.parent.uri}">
<f:param name="cat" value="#{product.category.parent.uri}" />
</c:if>
The other alternative, while not ideal, is to conditionally render two separate output links and determine which to render based on your value. Such as:
<c:choose>
<c:when test="#{empty product.category.parent.uri}">
<h:outputLink value="product.xhtml">
<f:param name="cat" value="#{product.category.uri}"/>
<f:param name="product" value="#{product.uri}" />
</h:outputLink>
</c:when>
<c:otherwise>
<h:outputLink value="product.xhtml">
<f:param name="cat" value="#{product.category.parent.uri}" />
<f:param name="product" value="#{product.uri}" />
</h:outputLink>
</c:otherwise>
</c:choose>
Upvotes: 0