Menno
Menno

Reputation: 12651

JSF rendering: condition of EL statement incorrect

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

Answers (2)

BalusC
BalusC

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:

  1. The <f:param> does as being a tag handler not support the rendered attribute at all.

  2. 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">

See also


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

Bionic_Geek
Bionic_Geek

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

Related Questions