jsf set ui:param or use a value that changes dynamically in ui:repeat - jsf

I want to update an ui:param value or use something similar in ui:repeat loops with a condition. The idea is something as follows (is just an aprox, not the final implementation but I guess is understandable):
<ui:param name="marginLeftNode" value="#{myBean.initialMarginValue}" />
<ui:repeat value="#{myBean.viewWrapper.linkMap.entrySet().toArray()}" var="map">
<ui:repeat var="link" value="#{map.value}" varStatus="status">
<li style="margin-left: #{marginLeftNode}%" class="ico-#{link.getStyleCss()}-ayuda">
#{link.getTitle()}
</li>
<!-- Code conditions that doesn't works in any way as I've readed in the links after code -->
<c:if test="#{marginLeftNode gt 4}">
<ui:param name="marginLeftNode" value="#{myBean.viewWrapper.nodeList.get(status.index).depth}" />
</c:if>
<ui:fragment rendered="#{marginLeftNode gt 4}">
<ui:param name="marginLeftNode" value="#{myBean.viewWrapper.nodeList.get(status.index).depth}" />
</ui:fragment>
<!-- End code conditions: these are the two solutions c:if and ui:fragmen I tried -->
</ui:repeat>
</ui:repeat>
I can't use c:if inside ui:repeat because doesn't works (Specify conditional rendering of element inside <ui:repeat>? The <c:if> does not seem to work) and I can't use ui:fragment with ui:param because it doesn't works too (Conditional variable definition in JSF)
So, any idea how to solve that?

First of all, avoid of use JSTL if you can use JSF. Here is an answer that explains how JSTL and JSF are executed in different steps -> https://stackoverflow.com/a/3343681/4253629
To redefine conditionally a ui:param, for example, you can do this:
<ui:param
name="marginLeftNode"
value="#{marginLeftNode gt 4 ? myBean.viewWrapper.nodeList.get(status.index).depth : marginLeftNode }"/>
Probably exists another solution, but this works.
Greetings

Changing the ui:param inside the loop has no real value. The real purpose of a ui:param is to pass runtime variables into a template client or included file. By the time your parameter has been passed in, there's little value in changing it. If all you want is to conditionally alter the value of the variable after it's been passed, you could just use JSTL's c:set to set a page-scoped variable that you could then use
<ui:repeat value="#{myBean.viewWrapper.linkMap.entrySet().toArray()}" var="map">
<ui:repeat var="link" value="#{map.value}" varStatus="status">
<li style="margin-left: #{marginLeftNode}%" class="ico-#{link.getStyleCss()}-ayuda">
#{link.getTitle()}
</li>
<c:if test="#{marginLeftNode gt 4}">
<c:set var="marginLeftNode" value="#{myBean.viewWrapper.nodeList.get(status.index).depth}"/>
</c:if>
</ui:repeat>
You could then access your set variable as #{marginLeftNode} anywhere within that view

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 *'}" />

Is it possible to use <c:if> inside <a4j:repeat>

Is it possible to use <c:if> inside <a4j:repeat>?
I need to render a <br /> each 4 elements. I tried the following approach:
<a4j:repeat value="#{MBean.sucursal.events}" var="item" rowKeyVar="idx" >
<h:outputText value="#{item.eventDescription}" id="item1" />
<c: if test=#{idx % 4 == 0}>
<br />
</c:if>
</a4j:repeat>
However, it does not render the <br /> elements at all.
How can I achieve the requirement anyway?
Note: I'll ignore the careless formulation of the code which causes that it wouldn't compile/run properly at all. There's a space in <c :if> and the quotes are missing from the <c:if test> attribute. In future questions please pay a bit more attention to the properness of the code snippets you post, otherwise you'll only get invalid answers due to those red herrings.
Coming back to your concrete problem, this construct will indeed fail. The <c:if> runs during view build time (when the JSF component tree is about to be populated based on Facelets/JSP files), while the <a4j:repeat> runs during view render time (when the HTML code is about to be generated based on the JSF component tree). So, at the moment the <c:if> runs, #{idx} does not exist in the scope at all and always evaluates to null.
There are basically 2 solutions:
Use <c:forEach> instead of <a4j:repeat> to iterate over the items. It runs during view build time, so it'll run "in sync" with <c:if>.
<c:forEach items="#{MBean.sucursal.events}" var="item" varStatus="loop">
<h:outputText value="#{item.eventDescription}" id="item#{loop.index}" />
<c:if test="#{loop.index % 4 == 0}">
<br />
</c:if>
</c:forEach>
(note that I changed the <h:outputText id> as well, otherwise you end up with duplicate component ID errors or malformed HTML; feel free to omit the id attribute altogether, JSF will autogenerate proper one)
Use JSF component's rendered attribute instead of <c:if> to conditionally render HTML. It runs during view render time, so it'll run "in sync" with <a4j:repeat>.
<a4j:repeat value="#{MBean.sucursal.events}" var="item" rowKeyVar="idx">
<h:outputText value="#{item.eventDescription}" id="item1" />
<h:panelGroup rendered="#{idx % 4 == 0}">
<br />
</h:panelGroup>
</a4j:repeat>
See also:
JSTL in JSF2 Facelets... makes sense?

Conditionally render ui:define

How do I conditionally render a <ui:define>?
The data in the template depends on a required <f:viewParam>.
But if an invalid view parameter is provided, then the <ui:define> should not be rendered since the default content of the template should be used.
I tried using <c:if> but it is not working.
It's not possible to conditionally render/build an <ui:define>. You can only do for its contents. Anything outside <ui:define> is ignored in templates.
Better is to conditionally build the <ui:insert> instead. The <ui:insert> runs during view build time, so it's not possible to conditionally render it by a property which is set via <f:viewParam>. You can only conditionally build it (the <ui:insert> tag itself) using a conditional view build time tag such as JSTL <c:if> which in turn checks the raw request parameter directly (and thus not the <f:viewParam> property).
In the master template, it would look like this, assuming that just the sole presence of the request parameter is sufficient (if you need to perform validation, you'd need to do it inside the EL expression, or in a managed bean wherein the property is preinitialized via #ManagedProperty("#{param.foo}").
E.g., in the master template:
<c:if test="#{not empty param.foo}">
<ui:insert name="content" />
</c:if>
<c:if test="#{empty param.foo}">
<p>Default content</p>
</c:if>
or, more clear but more verbose
<c:choose>
<c:when test="#{not empty param.foo}">
<ui:insert name="content" />
</c:when>
<c:otherwise>
<p>Default content</p>
</c:otherwise>
</c:choose>
See also:
JSTL in JSF2 Facelets... makes sense?
Use <ui:fragment> instead.
<ui:define name="description">
<ui:fragment rendered="false">
<meta name="description" content="do not render" />
</ui:fragment>
</ui:define>
Duplicate of ui:define with rendered="false" attribute still rendering

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>

Reuse variable outside of composition - passing variables up in jsf?

I'm using jsf 1.2 and have got some weird javascript which gets repeated at several places in my project.
I do not want to repeat myself.. but i found no way to do something similar to this:
<ui:composition>
<c:set var="confirmBlockJS" value="if (confirm('#{tk:encodeJS(confirmMessage)}')) {#{confirmed}} else{ #{notConfirmed}}"/>
</ui:composition>
Now i want to use the set variable but obv this will fail.
<ui:include src="/blocks/tech/confirmBlock.xhtml">
<ui:param name="confirmMessage" value="#{appTexts['action.logout.title']}"/>
<ui:param name="confirmed" value="return false;"/>
<ui:param name="notConfirmed" value=" #{doJS}"/>
</ui:include>
<h:outputLink id="logout" value="#" rendered="#{renderLogout}"
onclick='#{confirmBlockJS};'
styleClass="_allowEnterKey">
<h:outputText value="#{appTexts['action.logout']}"></h:outputText>
</h:outputLink>
confirmBlockJS is obviously empty at that point, which is actually in my eyes a good thing. But is there no way to pass the variable up? I tried ui:param without luck.
SOLUTION
Use ui:decorate when you want to access variable set with c:set outside of the page which uses the ui:decorate. It will still be visible. In the case of ui:include the c:set will not be visible outside, but ui:param should pass it up (e.g. use ui:param instead of c:set in the ui:include composition)
If you didn't want to inline it as an attribute value, then <ui:decorate> would be the solution for you.
An EL function is closest what you can get to work for this particular functional requirement:
<h:outputLink id="logout" value="#" rendered="#{renderLogout}"
onclick="#{js:confirmBlock(appTexts['action.logout.title'], 'return false;', doJS)}"
styleClass="_allowEnterKey">
<h:outputText value="#{appTexts['action.logout']}"></h:outputText>
</h:outputLink>
or:
<ui:param name="confirmBlock" value="#{js:confirmBlock(appTexts['action.logout.title'], 'return false;', doJS)}" />
<h:outputLink id="logout" value="#" rendered="#{renderLogout}"
onclick="#{confirmBlock}"
styleClass="_allowEnterKey">
<h:outputText value="#{appTexts['action.logout']}"></h:outputText>
</h:outputLink>
Note that generating JS code by Java/JSF/EL this way is a bit a smell. Is it really not possible to make it a simple JS function which you put in a .js file and parameterize it with arguments?

Resources