JSF How to evaluate c:set only once at declaration place - jsf

I was using c:set as expression alias. Today I encountered, that c:set is evaluated everytime var is being used and also on each "invocation" it's output changes - depends on current context:
<!-- lets say composite has id "composite" -->
<ui:composition ...>
<cc:implementation>
<c:set var="compositeClientId" value="#{component.namingContainer.clientId}"/>
<!-- OUTPUTS "composite" -->
<h:outputText value="#{compositeClientId}"/>
<h:form id="form">
<!-- OUTPUTS "composite:form" -->
<h:outputText value="#{compositeClientId}"/>
</h:form>
<cc:implementation>
</ui:composition>
Is there any way, how to evaluate c:set at declaration place so in both cases it would output "composite"?

Related

jsf make ui:param result static

I am using templating and pass some values by to use them in the template:
<ui:repeat var="entry" value="#{showEntriesBean.entries}" id="repeatId">
<ui:include src="/templates/entryTemplate.xhtml">
<ui:param name="prePath" value="/" />
<ui:param name="allowedToSee" value="#{bean.calcRandom(0, 10)}" />
</ui:include>
<br />
</ui:repeat>
I found out, that everytime I use the "allowedToSee" variable in my entryTemplate.xhtml, it recalculates its value.
Is there any way to pass the result of calcRandom in a static way? So it is ONCE calculated (when the allowedToSee value is calculated) and performs like a final number? I don't want it to be calculated everytime #{allowedToSee} is used
If you want the value to be held, you should held it somewhere.
What I would do:
1) Create a List<Integer> property.
2) When the values of the entries properties are set, call a method (preRenderEvent? #PostConstruct?) that introduces in your list as many items as there are in entries.
3) Consult that list using the varStatus attribute of ui:repeat
Little more of less, like:
<ui:repeat var="entry" value="#{showEntriesBean.entries}" id="repeatId" varStatus="status">
<ui:include src="/templates/entryTemplate.xhtml">
<ui:param name="prePath" value="/" />
<ui:param name="allowedToSee" value="#{bean.getPrecalculedRandomAt(status.index)}" />
</ui:include>
<br />
</ui:repeat>
More on the varStatus attribute: http://docs.oracle.com/javaee/6/javaserverfaces/2.0/docs/pdldocs/facelets/ui/repeat.html

<c:when test> evaluates always false

This is called 3 times, for each row once. (example table has 3 rows)
....
<ui:param name="rowIndex" value="#{cc.attrs.rowIndex}" />
<ui:param name="rowActive" value="#{cc.attrs.activeRow}" />
<c:set var="index" value="#{rowIndex}" type="java.lang.Long"/>
<c:set var="activeRowIndex" value="#{rowActive}" type="java.lang.Long"/>
<c:choose>
<c:when test="${index == 2}">
ACTIVE
</c:when>
<c:when test="${index != activeRowIndex}">
${index} - ${activeRowIndex} - INACTIVE
</c:when>
<c:otherwise>
NONE
</c:otherwise>
</c:choose>
....
Result:
0 - 1 - INACTIVE
1 - 1 - INACTIVE
2 - 1 - INACTIVE
I would have expected:
0 - 1 - INACTIVE
NONE
ACTIVE
I'm quite clueless why the result is so different from what i expected.
So i hope you can help me :-)
The variable names used suggests that you're using the composite inside a repeating component, such as <h:dataTable> or <ui:repeat>.
JSTL tags are executed during view build time, that moment when the JSF component tree is built based on XHTML source code. However, the var attribute of a repeating component is only available during view render time, that moment when the HTML output is produced based on JSF component tree.
In effects, at least the #{cc.attrs.rowIndex} is always null when JSTL runs.
When you're dependent on conditions which are only available during view render time, then you should be using the rendered attribute of a JSF component instead of JSTL <c:choose>/<c:if>.
E.g.
<c:set var="active" value="#{cc.attrs.rowIndex == 2}" />
<c:set var="inactive" value="#{not active and cc.attrs.rowIndex != cc.attrs.activeRow}" />
<c:set var="none" value="#{not active and not inactive}" />
<h:outputText value="ACTIVE" rendered="#{active}" />
<h:outputText value="#{index} - #{activeRowIndex} - INACTIVE" rendered="#{inactive}" />
<h:outputText value="NONE" rendered="#{none}" />
Note that this problem doesn't affect the <c:set>. It merely creates a EL variable mapping (an "alias"), it doesn't immediately evaluate the EL expression and store its result somewhere (as long as scope isn't definied). Also note that ${} and #{} behave exactly the same when Facelets is used instead of JSP. As the ${} is basically a heritage of legacy JSP, you should prefer exclusively using #{} to avoid confusion by yourself and your future maintainers.

c:choose with multiple c:when - fall-through switch

If i have this code on my jsf page:
<c:choose>
<c:when test="${empty example1}">
</c:when>
<c:when test="${empty example2}">
</c:when>
<c:otherwise>
</c:otherwise>
</c:choose>
it will work like java switch statement with case and break - if first when is true, second won't be test, right ?
What should i write to get "switch statement with case but without break" :
when first C:when is true something add to page and when second is true something is add to page too
You're thus basically looking for a fall-through switch. That isn't possible with <c:choose> as it represents a true if-else.... JSTL does not offer any tags for a fall-through switch.
Your best bet is to use multiple <c:if>s wherein you also check the preceding condition as an or condition.
<c:if test="#{empty example1}">
...
</c:if>
<c:if test="#{empty example1 or empty example2}">
...
</c:if>
<c:if test="#{empty example1 or empty example2 or empty example3}">
...
</c:if>
...
As you're using JSF, an alternative is using component's rendered attribute.
<h:panelGroup rendered="#{empty example1}">
...
</h:panelGroup>
<h:panelGroup rendered="#{empty example1 or empty example2}">
...
</h:panelGroup>
<h:panelGroup rendered="#{empty example1 or empty example2 or empty example3}">
...
</h:panelGroup>
...
The difference is that it's evaluated during view render time instead of during view build time. So if you were for example using this inside a <h:dataTable> based on the currently iterated row, the <c:if> wouldn't have worked the way you'd expect. See also JSTL in JSF2 Facelets... makes sense?
To eliminate the condition checking boilerplate, you could use <c:set> to create new EL variables. This works in both approaches.
<c:set var="show1" value="#{empty example1}" />
<c:set var="show2" value="#{show1 or empty example2}" />
<c:set var="show3" value="#{show2 or empty example3}" />
<h:panelGroup rendered="#{show1}">
...
</h:panelGroup>
<h:panelGroup rendered="#{show2}">
...
</h:panelGroup>
<h:panelGroup rendered="#{show3}">
...
</h:panelGroup>
...
I think it isn't possible with c:when. You can use c:if instead:
<c:if test="${empty example1}">
"example1 empty"
</c:if>
<c:if test="${empty example2}">
"example2 empty"
</c:if>
<c:if test="${not empty example1 and not empty example2}">
"both not empty"
</c:if>

Variable set by ui:param or c:set inside ui:repeat is not available outside ui:repeat

I would like to define a variable and reuse it somewhere else on the page. I'm defining variable in JSF using <ui:param> and <c:set> and resetting it in the <ui:repeat> depending on condition as follows:
<ui:param name="title" value="default value"/>
<c:set var="title2" value="default val"/>
<ui:repeat value="#{aClip.revisions}" var="revs" varStatus="revStat">
<ui:repeat value="#{revs.metadataMap.entry}" var="entryLst">
<ui:repeat value="#{entryLst}" var="entry">
<ui:repeat value="#{entry.value}" var="metaVal">
<ui:repeat value="#{metaVal.values}" var="aValue">
<ui:fragment rendered="#{metaVal.key eq 'Title'}">
rendered //this prints. condition is evaluted to true
<ui:param name="title" value="#{aValue}"/>
<c:set var="title2" value="#{aValue}"/>
title2 -- #{title2} title -- #{title} //prints
</ui:fragment>
</ui:repeat>
</ui:repeat>
</ui:repeat>
</ui:repeat>
</ui:repeat>
title2 -- #{title2} title -- #{title} //does not print value not even defualt val
I know condition is true and value is set, but when I try to use it outside loop, it doesn't evalute. It doesn't even evalute to defualt value given to variable while defining.
How is this caused and how can I solve it?

Dynamically build the column values

I am building a component that will build a data table dynamically. I need to pass in the field name from the class and concatenate it to the var attribute of the table. example : "tblVar.firstName". I have tried this using a ui:param as the below code shows, but it just prints the string expression it doesn't evaluate the firstName.
Is there a way to take a string and turn it into an EL expression.
<composite:interface>
<composite:attribute name="pageBean" type="pagecode.app.Maintenence" required="true"/>
<composite:attribute name="dataTableList"/>
<composite:attribute name="columnHeader"/>
<composite:attribute name="columnFieldName"/>
</composite:interface>
<composite:implementation>
<p:dataTable id="doc_code_table" value="#{cc.attrs.pageBean.documentCodeList}"
var="tblVar" rowIndexVar="index" paginator="false">
<ui:param value="#{tblVar}.#{cc.attrs.columnFieldName}" name="colValue"/>
<p:column headerText="#{cc.attrs.columnHeader}">
<h:outputText value="#{colValue}"/>
</p:column>
</p:dataTable>
</composite:implementation>
You're there indeed creating a string variable. The effect is exactly the same as when you do this:
<h:outputText value="#{tblVar}.#{cc.attrs.columnFieldName}" />
This is not right. You should use the brace notation #{bean[property}} to use a variable as property name. Thus, so:
<h:outputText value="#{tblVar[cc.attrs.columnFieldName]}"/>
See also:
Our EL wiki page

Resources