2

I'm trying to build a custom component from existing Primefaces components and I need some help here. I have a selectOneMenu and I want it to render or not (and disable or enable) other components according to which option is selected in the menu. The hard thing here is that I can't do it using a Managed Bean (I have a few reasons for that), I need a pure xhtml code.

I tried some <c:choose> and <ui:parameter> stuff to create booleans but for some reason that I can't see it's not working. Could you guys take a look at my code and see if you have any ideas? It may be something simple that I can't figure or something I don't know yet.

<h:body>
<h:form id="abc">
    <ui:repeat var="pd" value="#{produtoMB.produtos}">
        <h:panelGroup id="linha">
        <c:choose>
            <c:when test="#{pd.marca == X1}">
                <c:set var="render" value="#{true}" />
            </c:when>
            <c:otherwise>
                <c:set var="render" value="#{false}" />
            </c:otherwise>
        </c:choose>

        <p:selectOneMenu value="#{pd.marca}">
            <f:selectItem itemLabel="Select One" itemValue="" noSelectionOption="true"/>
            <f:selectItem itemLabel="Xbox One" itemValue="X1" />
            <f:selectItem itemLabel="PlayStation 4" itemValue="PS4" /> 
            <f:selectItem itemLabel="Wii U" itemValue="WU" />
            <p:ajax update="linha" />
        </p:selectOneMenu>
        <p:inputText value="#{pd.aparelho}" disabled="#{render}"/>
        <h:outputText value="Microsoft" rendered="#{render}"/>
        <p:commandButton value="X" />
        </h:panelGroup>
        <br />
    </ui:repeat>
    <br />
    <p:commandButton actionListener="#{produtoMB.botaoMais}" value="+" update="abc"/>
    <p:commandButton actionListener="#{produtoMB.botaoMenos}" value="-" update="abc"/>
</h:form>

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Denis Klein
  • 101
  • 1
  • 11

1 Answers1

0

There are a couple of issues here.

First,

<ui:repeat ...>
    <c:choose>
        ...
    </c:choose>
</ui:repeat>

JSTL tags run during view build time. It's executed only once before all JSF component runs. So, on contrary to what you'd intuitively expect from the XML code flow, it isn't executed when the <ui:repeat> component runs and iterates. See also JSTL in JSF2 Facelets... makes sense?

Second,

<c:when test="#{pd.marca == X1}">
...
<f:selectItem ... itemValue="X1" />

The itemValue="X1" represents a String. The EL expression #{X1} basically looks for a variable named X1 in respectively the facelet, request, view, session, and application scopes until the first non-null value is found. In order to represent a String value in EL, you need to quote it with singlequotes like so #{'X1'}. So, you should have used #{pd.marca == 'X1'} instead. Nonetheless, this still won't work for the reason mentioned in the first point. See also Specify conditional rendering of element inside <ui:repeat>? The <c:if> does not seem to work.

You can however use <c:set> to create an alias in the EL scope, as long as you don't set its scope attribute. See also Defining and reusing an EL variable in JSF page.

After removing JSTL <c:choose> and fixing the EL expression #{X1} to represent a real String literal #{'X1'}, here's how it should look like:

<ui:repeat var="pd" value="#{produtoMB.produtos}">
    <p:selectOneMenu value="#{pd.marca}">
        <f:selectItem itemLabel="Select One" itemValue="#{null}" />
        <f:selectItem itemLabel="Xbox One" itemValue="X1" />
        <f:selectItem itemLabel="PlayStation 4" itemValue="PS4" /> 
        <f:selectItem itemLabel="Wii U" itemValue="WU" />
        <p:ajax update="linha" />
    </p:selectOneMenu>
    <h:panelGroup id="linha">
        <c:set var="marcaIsX1" value="#{pd.marca eq 'X1'}" />
        <p:inputText value="#{pd.aparelho}" disabled="#{marcaIsX1}" />
        <h:outputText value="Microsoft" rendered="#{marcaIsX1}" />
    </h:panelGroup>
    <p:commandButton value="X" />
</ui:repeat>

Note that I also removed the unused noSelectionOption="true" and moved the <h:panelGroup id="linha"> to wrap only the components which really need to be updated.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Balus, thank you so much for your answer. It is indeed a solution that makes more sense (and for some reason I didn't even consider the idea of writting the conditions in each "disabled" and "rendered" attributes. I noticed my mistake in the EL expression before I got your answer, but thanks for that also! I would really like the to work as I thought, but we can't have all we want in this life, right? ;-) Thank you so much, you saved the day! – Denis Klein Sep 22 '15 at 13:17
  • Baulus, do you think there might be a way to create boolean variables and use them as I intended the first time, maybe with something different from the JSTL tags? Just a curiosity: I solved my problem for now, but I'm thinking of different scenarios in which I would need to get back to my original idea... – Denis Klein Sep 22 '15 at 13:35
  • Just use `` without the whole `` block. See also http://stackoverflow.com/q/6434866 – BalusC Sep 22 '15 at 13:37