9

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?

Francesco
  • 2,350
  • 11
  • 36
  • 59
  • 2
    You're not clear on when exactly it works/fails. The ``s look fine, but `` is completely misplaced. It belongs in a ``. Also the code seems to be incomplete, this appears to be a template client, but I'm nowhere seeing an ``. You do know that anything outside `` and `` is ignored by Facelets? – BalusC Dec 04 '13 at 12:15
  • The `` is there (see the first row of the code) so it shouldn't be ignored. I removed the `` and added a `c:if` instead. but it still doesn't work... As said, with the ternary operator no-problems, but with the `c:if` nope... (I edited my code, as the value for the title was wrong) – Francesco Dec 04 '13 at 13:10
  • @ BalusC Ok, the `` is defined below. I shifted the `c:if`code there and now it works! But can you now explain me why with the tern. op. works although it is outside the ``? Besides I have others `ui:param` defined outside it (just below the `c:if`) and they work... – Francesco Dec 04 '13 at 13:22
  • @BalusC Can you please put your comment into an answer, so that I can accept it, please? – Francesco Dec 04 '13 at 13:24

2 Answers2

12

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
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 3
    "... is in turn technically wrong and works in Mojarra only, but not in MyFaces ..." That's not true. Instead, it is the opposite. It is a looooonnngggg story, but I have written the reasons in [MYFACES-3169](https://issues.apache.org/jira/browse/MYFACES-3169). Recently in [MYFACES-3810](https://issues.apache.org/jira/browse/MYFACES-3810) it was added in 2.2.x branch a flag to enable the old behavior. ui:param only works if is a direct child of ui:include, ui:decorate or ui:composition but the params are available in the context of the target template. Use them inside c:if is a bad idea. – lu4242 Dec 04 '13 at 19:20
  • 1
    @lu: by the way I also agree that we should really keep the ``. Its ability to store the evaluated value in a fixed scope (request/view/session/application) is very appreciated. The `` doesn't support it (expression is re-evaluated on every access) and also the tag name "param" is at its own not really self-documenting as to the functional requirement of "setting a variable in the EL context". – BalusC Dec 04 '13 at 20:13
  • 1
    absolutely, c:set is indeed very useful. What we did in MyFaces was fix c:set to use page scope when no scope is set like in JSP and create a "template context" to store ui:param variables. Both c:set and ui:param still relies on EL VariableMapper facility and are considered build view time tags. Without this fix, some optimizations like EL caching cannot be done properly. Really ui:param is a tag used to pass a parameter as an EL Expression to the template. – lu4242 Dec 04 '13 at 23:20
2

You are using JSTL with Facelets. JSTL are executed during view build time, and not in render phase. Additionally there some issues with processing them in JSF2 libraries - like in older Mojarra versions, where they didn't work on view scoped beans with partial state saving - see https://stackoverflow.com/a/3343681). This is why your EL expression has worked.

The solution is to avoid JSTL - use ui:repeat instead c:forEach and EL expression and conditional rendering instead of c:if.

Community
  • 1
  • 1
Web Devie
  • 1,207
  • 1
  • 13
  • 30
  • Thank you for your explanation, but how can I use conditional rendering with ui:param? – Francesco Dec 04 '13 at 10:33
  • @Francesco primefaces components, as well as ui:fragment have 'rendered' attribute, which takes EL expression. – Web Devie Dec 04 '13 at 10:36
  • Ok, I see the ui:param has the rendered attribute too (I didn't thought...) – Francesco Dec 04 '13 at 10:55
  • Mmm, now I have `` (three times for the different possibilities), but it doesn't work. Now the last of the three is always choosen... – Francesco Dec 04 '13 at 11:03
  • I also tried to specify **true**, **false**, **false** instead of the checking EL, but although the last is false, it is the one which is "rendered" – Francesco Dec 04 '13 at 11:11
  • @Francesco ui:param doesn't have rendered attribute. General advice: if you need to have more complicated expression in EL, consider creating a method in bean that would return what you need. – Web Devie Dec 04 '13 at 11:19
  • I tried with ` ` but it doesn't work too... Sorry but now I don't have any other ideas/jsf-knowledge to solve the problem using your tip to use EL and cond. rendering to replace c:if... – Francesco Dec 04 '13 at 11:28
  • I will remove the downvote if you explain "many bugs" in detail. Usually, those are namely just developer's own mistakes/misunderstandings. In the meanwhile, perhaps you want to read http://stackoverflow.com/questions/3342984/jstl-in-jsf2-facelets-makes-sense first. – BalusC Dec 04 '13 at 12:14
  • @BalusC Sorry BalusC, but I don't understand what do you mean with the first two sentences. – Francesco Dec 04 '13 at 12:26
  • Comment is targeted on answer of web devie not on your question. – BalusC Dec 04 '13 at 12:31
  • @BalusC actually, I've seen that post, well, maybe it's not a bug but issue, anyway I've seen also somewhere in your answer that you've opted against using JSTL. – Web Devie Dec 04 '13 at 12:34
  • @BalusC Ah, ok :) Any tip on how to solve the problem? What I need is really just a way to choose between three possibilities... :( – Francesco Dec 04 '13 at 12:38
  • Just start giving feedback on my comment on your question. – BalusC Dec 04 '13 at 12:39
  • @BalusC One think is not clear to me... In your answer(the linked one) you said `...The view build time is that moment when the XHTML/JSP file is to be parsed and converted to a JSF component tree ...` and then `Use JSTL tags to control flow of JSF component tree building...`. But what I did in my (original) question is not that? The `c:if` doesn't "control/say" which of the three possibilities should be in the JSF component tree? – Francesco Dec 04 '13 at 12:43
  • I didn't mean to give feedback on my comment on this answer, but on your own question. Scroll back to top to your question. Look at the bottom of your question. There's a comment on that. Give feedback to that. All comments on a certain post are targeted on the poster of that post, not to someone else. – BalusC Dec 04 '13 at 12:48