Search code examples
jsf-2primefacesfacelets

Setting ui:param conditionally


I want to set a ui:param depending on a bean value and I thought using c:if was a good idea. So I put in my page the following code:

<ui:composition 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"
    xmlns:wai="http://www.id.ethz.ch/wai/jsf"
    template="/view/listView.xhtml">

        <c:if test="#{subscriptionListController.model.listViewName eq 'mySubscriptions'}">
         <ui:param name="title" value="#{msg.subscriptionTitleMySubscriptions}"/>
        </c:if>
        <c:if test="#{subscriptionListController.model.listViewName eq 'paidSubscriptions'}">
         <ui:param name="title" value="#{msg.subscriptionTitlePaidSubscriptions}"/>
        </c:if>
        <c:if test="#{subscriptionListController.model.listViewName eq 'allSubscriptions'}">
         <ui:param name="title" value="#{msg.subscriptionTitleAllSubscriptions}"/>
        </c:if>
        ....

but the parameter is not set...

If I let print out the value of #{subscriptionListController.model.listViewName eq 'mySubscriptions'} I get true in the corresponding case and false in the other two cases.

At the beginning I had only 2 possibilities and solved it with the ternary operator:

<ui:param name="title" value="#{subscriptionListController.model.listViewName eq 'mySubscriptions' ? msg.subscriptionTitleMySubscriptions : msg.subscriptionTitlePaidSubscriptions}"/>

and it worked. But now I have more possibilities...

What am I doing wrong?


Solution

  • As indicated by <ui:composition template>, this page represents a template client.

    Any <ui:param> outside <ui:define> applies to the master template (the file which you declared in template attribute) and is ignored inside the template client itself. If you intend to prepare variables for inside the template client, you should put <ui:param> inside <ui:define>.

    But there's another thing: the original purpose of <ui:param> is to pass variables to the file referenced by <ui:composition template>, <ui:decorate template> or <ui:include src>, not to prepare/set variables inside the current facelet context. For the sole functional requirement of preparing/setting variables in the current EL context, you'd better be using JSTL <c:set> for the job. You can use <ui:param> for this, but this isn't its original intent and didn't work that way in older MyFaces versions.

    Thus, so:

    <ui:define>
        <c:if test="#{subscriptionListController.model.listViewName eq 'mySubscriptions'}">
            <c:set var="title" value="#{msg.subscriptionTitleMySubscriptions}"/>
        </c:if>
        <c:if test="#{subscriptionListController.model.listViewName eq 'paidSubscriptions'}">
            <c:set var="title" value="#{msg.subscriptionTitlePaidSubscriptions}"/>
        </c:if>
        <c:if test="#{subscriptionListController.model.listViewName eq 'allSubscriptions'}">
            <c:set var="title" value="#{msg.subscriptionTitleAllSubscriptions}"/>
        </c:if>
        ...
    </ui:define>
    

    Unrelated to the concrete problem, you can optimize this as follows without the need for an unmaintainable <c:if> group which would only grow with every subscription type:

    <ui:define>
        <c:set var="subscriptionTitleKey" value="subscriptionTitle.#{subscriptionListController.model.listViewName}">
        <c:set var="title" value="#{msg[subscriptionTitleKey]}"/>
        ...
    </ui:define>
    

    with those keys

    subscriptionTitle.mySubscriptions = Title for my subscriptions
    subscriptionTitle.paidSubscriptions = Title for paid subscriptions
    subscriptionTitle.allSubscriptions = Title for all subscriptions