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

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>

Related

<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.

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?

How to show JSF components if list is not null and has size() > 0

How do I show JSF components if a list is not null and it has a size() > 0?
EL offers the empty operator which checks both the nullness and emptiness of an object.
Thus, this should do:
<h:dataTable value="#{bean.list}" var="item" rendered="#{not empty bean.list}">
No need for a clumsy double check on both null and size() as suggested by other answers.
See also:
How do I display a message if a jsf datatable is empty?
Conditionally displaying JSF components
use rendered attribute. most of the components have this attribute.This attribute;s main purpose is to render components conditionally.
<h:dataTable value="#{bean.list}" rendered="{bean.list !=null && bean.list.size()>0}" >
In the above piece of jsf code, datatable would only be rendered when list is not null and the size of list is greater than 0
<h:outputText value="No Data to Display!" rendered="#{empty list1.List2}" />
<a href="#">
<h:outputText value="Data is present" rendered="#{not empty list1.List2}" /></a>
Or
<h:outputText value="#{not empty list1.List2 ? 'Data is Present' : 'No Data to Display'}" style="color:blue"/>

Best approach to render UI elements based on dataType

I am trying to render richfaces and jsf UI elements dynamically based on the dataType value.
Ex : I have a enum as below
public enum DataType {
DT_LONGLONG(1), DT_STRING(2), DT_LONG(3), DT_DATE(4), DS_EXTERNALREFERENCE(5),
DT_BOOLEAN(6), DT_FLOAT(7), DT_SHORT(8);
}
Then in xhtml page while iterating through the list of my custom objects, I check for the dataType and render the UI elements accordingly as below :
<c:if test="#{meaCompPartAttr.dataType.dataType == 2}">
<h:inputText />
</c:if>
<c:if test="#{(meaCompPartAttr.dataType.dataType == 1) or
(meaCompPartAttr.dataType.dataType == 3) or
(meaCompPartAttr.dataType.dataType == 8)}">
<h:inputText onkeyup="javascript:validateField(this, '#{tpMsgs.longRegularExpression}');">
<f:validateLongRange/>
</h:inputText>
</c:if>
<c:if test="#{meaCompPartAttr.dataType.dataType == 7}">
<h:inputText onkeyup="javascript:validateField(this, '#{tpMsgs.doubleRegularExpression}');">
<f:validateDoubleRange/>
</h:inputText>
</c:if>
<c:if test="#{meaCompPartAttr.dataType.dataType == 6}">
<h:selectBooleanCheckbox />
</c:if>
<c:if test="#{meaCompPartAttr.dataType.dataType == 4}">
<rich:calendar />
</c:if>
Because of this I usually get class cast exceptions like String to Boolean or Long to String etc. I assume this is happening coz jstl and jsf code do not run in sync.
Is there any other approach to render UI elements dynamically as proposed in the above sample?
So you're iterating using <ui:repeat> or <h:dataTable> or any other JSF iterating component instead of the JSTL <c:forEach>? Either use <c:forEach> instead, or use the rendered attribute instead of <c:if>.
See also:
JSTL in JSF2 Facelets... makes sense?
How to create dynamic JSF form fields

Concatenating strings within EL expression defined in an attribute of a facelets tag

I need to write an EL expression for an attribute which goes something like this:
#{cc.attrs.appreciatedByCurrentUser ? (cc.attrs.count +'<br/>'+ (cc.attrs.count-1)) : ((cc.attrs.count+1) +'<br/>'+ cc.attrs.count)}
Now the problem is that this gives an error as strings cannot be concatenated, the way I am doing it. So how can I rectify this?
I'm using JSF 2.0 with facelets.
EDIT :
I'm resolving the issue using the following inline javascript
<script type="text/javascript">
var count=#{cc.attrs.count};
document.write(#{cc.attrs.appreciatedByCurrentUser} ? (count-1) +'<br/>'+count : count+'<br/>'+ (count+1));
</script>
Can you think of any issue with this?
It is possible to concatenate Strings in EL using the java.lang.String.concat(String) method. Thus your code could look like:
<h:outputText value="#{cc.attrs.appreciatedByCurrentUser ? (''.concat(cc.attrs.count).concat('<br/>').concat(cc.attrs.count-1)) : (''.concat((cc.attrs.count+1)).concat('<br/>').concat(cc.attrs.count))}" escape="false" />
In this particular case however I would go with one of the options that Mr BalusC had suggested because the code above doesn't look quite elegant. In some cases knowing this technique could be useful, though.
I would hardly recommend using javascript as a workaround here.
String concatenation in EL is only possible by just inlining in the expression. The + operator is in EL exclusively a sum operator. Further, < and > are invalid characters in XML attributes, so you have to escape them (and instruct <h:outputText> to not escape them once again by escape="false"):
<h:outputText value="#{cc.attrs.count}<br/>#{cc.attrs.count-1}" escape="false" rendered="#{cc.attrs.appreciatedByCurrentUser}" />
<h:outputText value="#{cc.attrs.count+1}<br/>#{cc.attrs.count}" escape="false" rendered="#{!cc.attrs.appreciatedByCurrentUser}" />
Alternatively, you can also use <c:set> to alias the expression:
<c:set var="appreciated" value="#{cc.attrs.count}<br/>#{cc.attrs.count-1}" />
<c:set var="notAppreciated" value="#{cc.attrs.count+1}<br/>#{cc.attrs.count}" />
<h:outputText value="#{cc.attrs.appreciatedByCurrentUser ? appreciated : notAppreciated}" escape="false" />
This is the only thing i can come up with.
<h:panelGroup rendered="#{cc.attrs.appreciatedByCurrentUser}">
<h:outputText value="#{(cc.attrs.count)}" style="display:block;" />
<h:outputText value="#{(cc.attrs.count-1)}" />
</h:panelGroup>
<h:panelGroup rendered="#{not cc.attrs.appreciatedByCurrentUser}">
<h:outputText value="#{(cc.attrs.count+1)}" style="display:block;" />
<h:outputText value="#{(cc.attrs.count)}" />
</h:panelGroup>
Putting <br> in a value attribute will always throw errors in JSF, so you'll have to use display:block.

Resources