<h:selectOneMenu with conditional <f:selectItems shows options twice - jsf

I want to use a selectOneMenu to have a user choose a value. In some cases I want to disable one of the values shown in the menu. I tried using render on both the selectItems as well as selectOneMenu as well as added a ui:fragment around the Menu but I always get all the values from both lists shown. Any ideas how to prevent that?
Here my current last try that again resulted in twice the list and the item in question once enabled and once disabled in it:
<ui:fragment rendered="#{cc.attrs.showP==true}">
<h:selectOneMenu id="type" binding="#{cc.type}">
<f:selectItems value="#{typeDAO.findAll()}"/>
</h:selectOneMenu>
</ui:fragment>
<ui:fragment rendered="#{cc.attrs.showP==false}">
<h:selectOneMenu id="type" binding="#{cc.type}">
<f:selectItems value="#{typeDAO.findAll()}" var="item" itemDisabled="#{item=='P'}"/>
</h:selectOneMenu>
</ui:fragment>

Your concrete problem is caused because you're binding physically multiple components to the same variable.
<h:selectOneMenu ... binding="#{cc.type}" />
<h:selectOneMenu ... binding="#{cc.type}" />
If the getter behind binding returns non-null, then JSF will use it instead of creating a new one. Basically, the second tag will reuse the component created in the first tag and set/add all attributes/items to it.
Your particular case can be solved in at least two ways:
Use JSTL to build the JSF component tree conditionally instead of using JSF to render the HTML output conditionally. You shouldn't have physically multiple components in the JSF component tree sharing the same binding let alone the same id.
<c:if test="#{cc.attrs.showP}">
<h:selectOneMenu id="type" binding="#{cc.type}">
...
</h:selectOneMenu>
</c:if>
<c:if test="#{not cc.attrs.showP}">
<h:selectOneMenu id="type" binding="#{cc.type}">
...
</h:selectOneMenu>
</c:if>
Make your code DRY. I.e. get rid of all code duplication.
<h:selectOneMenu id="type" binding="#{cc.type}">
<f:selectItems value="#{typeDAO.findAll()}" var="item" itemDisabled="#{not cc.attrs.showP and item eq 'P'}" />
</h:selectOneMenu>
See also:
How does the 'binding' attribute work in JSF? When and how should it be used?
JSTL in JSF2 Facelets... makes sense?

Guess I found it - bit weird to answer my question though. I think it's because the possible values of the menu are created before the rendered attributes are evaluated and since I did bind both menus to the same variable/id I got all items of the two menus. Thus I now used different names and then in my composite component have some logic that checks which one is used and continues to use the right value. Works :-)
Bit weird to me is this thing that as a developer u have to know when the attribute list is built in comparison to when the rendering happens. I recently had a similar issue with foreach and repeat. Is there any way to know these things as part of some overarching concept that I can remember or is that really case by case?
Thanks guys!

Related

Conditionally render a <div> based on value of <h:outputText>

I am using jsf primefaces. I want to display an image depending on the value of a specific outputext. If the text value is 'Alarm *' then a div will appear whith a spesific image. If the value is 'Alarm **' then a div with an other image will appear, etc. I tried the code below but it does not work for me.
<h:outputText id="alarmCriticalityValue" value="#{msg[summary.criticality.key]}" />
<c:if test="#{alarmCriticalityValue=='Alarm *'}">
<div class="alarm1"></div>
</c:if>
How should i implement this idea?
You need to use binding attribute to put the UIComponent instance in the EL scope. The id attribute doesn't do that, on contrary to what you expected.
<h:outputText binding="#{alarmCriticality}" ... />
And then you need to use UIOutput#getValue() to obtain its value attribute.
<c:if test="#{alarmCriticality.value == 'Alarm *'}">
That said, you'd better use rendered attribute here, particularly if #{summary} represents the currently iterated item of a JSF iterating component like <ui:repeat> or <h:dataTable>.
<h:panelGroup layout="block" styleClass="alarm1"
rendered="#{alarmCriticality.value == 'Alarm *'}" />
See also:
How does the 'binding' attribute work in JSF? When and how should it be used?
How to conditionally render plain HTML elements like <div>s?
JSTL in JSF2 Facelets... makes sense?
Unrelated to the concrete problem. It's strange to see the conditional rendering depend on localized text. What if you change the locale and/or the localized text? This is very brittle. You'd better check the bundle key instead.
<h:outputText value="#{msg[summary.criticality.key]}" />
<h:panelGroup layout="block" styleClass="alarm1"
rendered="#{summary.criticality.key == 'some.alarm.key'}" />
This way you also don't need to bind the output text anymore.
Try
<c:if test="#{msg[summary.criticality.key].equals('Alarm *')}">
Or add a binding to the h:outputText and check against that.
Try this
<h:outputText id="alarmCriticalityValue" value="#{msg[summary.criticality.key]}" />
<h:panelGroup layout="block" styleClass="alarm1" rendered="#{alarmCriticality.value eq 'Alarm *'}" />

How to do conditional update on a component

I have a JSF composite component which renders only some parts based on the attribute cc.attrs.list passed to it.
In one of the components I want to update a set of other components based on the attribute. So something like this:
<p:ajax event="dialogReturn" listener="#{cc.listener}"
update="#{cc.attrs.id2}_input #{cc.attrs.id2}_resultTable"/>
The problem is that the resultTable is not rendered all the time and when the resultTable is not there, I get an exception Cannot find component with expression "id_resultTable", which is not surprising. So my idea was to create a variable which will contain id of the attribute or empty String like this:
<c:if test="#{cc.attrs.list}">
<ui:param name="updateTable" value="#{cc.attrs.id2}_resultTable"/>
</c:if>
<c:otherwise>
<ui:param name="updateTable" value=""/>
</c:otherwise>
and then do the ajax update like this:
<p:ajax event="dialogReturn" listener="#{cc.listener}"
update="#{cc.attrs.id2}_input #{updateTable}"/>
The problem is, that the #{updateTable} variable is always an empty String(I've tried to put it as a content of outputText) and I have no idea why.
You can just omit the ui:param and do the check directly in the p:ajax:
<p:ajax event="dialogReturn" listener="#{cc.listener}"
update="#{cc.attrs.id2}_input #{cc.attrs.list ? cc.attrs.id2.concat('_resultTable') : ''}"/>
A problem with the c:if-approach could be, that when you update this section via ajax the condition is not re-checked as JSTL-tags are evaluated at view build time. Have a look at JSTL in JSF2 Facelets... makes sense? for further information.
This is not really a solution, but I've done a workaround for the problem which works. I've added h:panelGroup with id around the table and I'm updating this instead of the table.

JSF Ajax rerender <h:textInput ...> on Change

I am trying to get an h:textInput to rerender on change, I have tried it with a4j:ajax and f:ajax.
When using a4j:ajax:
<h:panelGroup id="xyzPG">
<ui:repeat var="var" ... >
...
<h:inputText id="#{idController.getIdXYZ(var.id)}"
value="#{someModel.value}"
size="3"
styleClass="#{errorController.getErrorStateStyleId(idController.getIdXYZ())}">
<a4j:ajax event="change" render="xyzPG" listener="#{listener.doSomeStuff}" />
</h:inputText>
...
</ui:repeat>
</h:panelGroup>
This works the first time, after the panel has updated the first time it stops updating the modell and the listener isn't called either. However the render is triggered causing the old values to be displayed.
Now when I replace a4j:ajax with f:ajax I get the error message that the id xyzPG cannot be found within xyzInput.
<f:ajax event="change" render="#this" listener="#{listener.doSomeStuff}" />
When I try limiting the rerender to the inputText it always updates the model and the listener is called, however the h:inputText is not rerendered.
I have already tried placing another panelGroup around the inputText but that didn't work either.
The reason why we are not using h:dataTable is because we have to produce a Table with the following layout:
----------------------------
| dataSet1 | dataSet 2 |
----------------------------
| dataSet3 | dataSet 4 |
etc...
Hence we are using the offset and step attributes of ui:repeat.
By design, you can't dynamically generate id attributes using a render-time tag like <ui:repeat/>
Compile-time V Render-time tags
For the purposes of view construction and ajax-updates, the ids of components must be available during view construction before a view is rendered. <ui:repeat/> already caters for uniqueness in the ids of it's child components. But if you require control of the ids, you need to use a compile-time tag handler like <c:forEach/>:
<c:forEach items="#{idController.itemsList}" var="theVar">
<h:inputText id="#{idController.getIdXYZ(theVar.id)}" value="#{someModel.value}" size="3" styleClass="#{errorController.getErrorStateStyleId(idController.getIdXYZ())}">
<a4j:ajax event="change" render="xyzPG" listener="#{listener.doSomeStuff}" />
</h:inputText>
<c:forEach/>
It's unlikely that you'll be able to reach xyzPG from within the <ui:repeat/> because the <ui:repeat/> is also a naming container, just like <h:panelGrid/> and so forth. The ajax update works with Richfaces because they've made special provisions for such use cases. <f:ajax/> however will not tolerate it. xyzPG is outside the scope of the <ui:repeat/> so to reach that component, you need to use a qualified naming convention:
<f:ajax event="change" render=":form1:xyzPG" listener="#{listener.doSomeStuff}"/>
This assumes that xyzPG is directly contained within an <h:form id="form1"/>
without seeing your backing bean code , I think you should use
<h:inputText id="#{idController.idXYZ}"
instead of
<h:inputText id="#{idController.getIdXYZ()}"

Can we create textbox right next to selectonemenu component when we fire selectoneMenu valueChangeListener event

want to create textbox on the fly is it possible?
Select Report to run:
<h:selectOneMenu value="#{reportBean.selectReport}">
<f:selectItems value = "#{reportBean.allReports}" />
<f:ajax listener="#{reportBean.getReqID}" render="reqID"> </f:ajax>
</h:selectOneMenu>
Seems like you want to show/hide the <h:inputText> based on the selected value on your <h:selectOneMenu>. Yes, this can be easily achieved with plain JSF.
Note that if you use set the rendered attribute as false the component won't appear in the component tree, so there will be no way it can't be referenced for any call (not even ajax calls). In order to update it, you should wrap the component inside another component like <h:panelGroup> and render the wrapper. Basic example:
<h:form id="frmRep">
<h:selectOneMenu value="#{reportBean.selectReport}">
<f:selectItems value = "#{reportBean.allReports}" />
<!--
assuming your reportBean.getReqID method will change the value of
reportBean.showReqID attribute to render/not render it and works well
-->
<f:ajax listener="#{reportBean.getReqID}" render="pnlRepName" />
</h:selectOneMenu>
<h:panelGroup id="pnlRepName">
<h:inputText id="reqID" rendered="#{reportBean.showReqID}"
value="#{reportBean.reportName}" />
<h:panelGroup>
</h:form>
For this specific requirement instead, I won't recommend using an ajax call since it has to go to the server to only check if the component should or should not be showed to the user. I would opt for a JavaScript solution to handle this just on client side.

Use c:set inside ui:repeat does not work

I have a JSF 2 application with the following code:
<c:set var="number" value="0" scope="view" />
<ui:repeat value="${items}" var="item" varStatus="itemIndex">
<c:if test="${item.boolProperty == true}">
<c:set var="number" value="${number + 1}" scope="view" />
</c:if>
<h:outputText value="#{item.name}" />: <h:outputText value="#{number}" />
</ui:repeat>
I want to increase the number depending on the property of the item in the loop. However, the condition seems not to work, since the value of number always stays 0. If I remove the condition, the number is incremented only once, or it is always 0 before incrementing, therefore it outputs 1. Could it be possible the number var changes not to affect the number var that is outside the loop? I believe the scope attribute takes care of that.
JSTL tags and JSF components doesn't run in sync as you'd expect from the coding. JSTL tags runs during building of the JSF view, while JSF components runs during rendering of the JSF view. See for a detailed explanation also JSTL in JSF2 Facelets... makes sense?
What you want to achieve is unfortunately not possible with JSF components. The <ui:param> comes close, but it functions merely as an alias for a more complex EL expression, while the <c:set> actually sets something in the desired scope (the view scope which you've there is by the way wrong).
Your best bet is to change the model or wrap the model in another model so that you end up as
<ui:repeat value="${items}" var="item" varStatus="itemIndex">
<h:outputText value="#{item.name}" />: <h:outputText value="#{item.number}" />
</ui:repeat>

Resources