The c:if test always fails for me and it never gets inside the loop. I am using the following namespaces
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:c="http://java.sun.com/jstl/core"
The string ('array') to be split is "Tom and Jerry are GAP1 friends"
<s:decorate template="/layout/display-text.xhtml">
<c:set var="array" value="#{_mybean.value}"/>
<c:set var="space" value="#{fn:split(array, ' ')}"/>
<c:set var="len" value="#{fn:length(space)}"/>
<h:outputText value="total length = #{len}"/><br/>
<c:forEach begin="0" end="5" var="index">
<h:outputText value="index = #{index}, value = #{space[index]}"/><br/>
<c:set var="val" value="#{space[index]}"/>
<c:if test="#{fn:startsWith(val, 'GAP')}">
<h:outputText value="Found keyword parameter GAP" /><br/>
</c:if>
</c:forEach>
</s:decorate>
The JSTL core URI is invalid. As per the JSTL TLD it should be (note the extra /jsp):
xmlns:c="http://java.sun.com/jsp/jstl/core"
That said, mixing JSF with JSTL is never been a good idea. It won't always give results as you'd expect because they doesn't run in sync as you would expect from the coding. It's more that JSP/JSTL runs from top to bottom first and then hands over the produced result to JSF to process further from top to bottom again. That would cause some specific constructs to fail. Better use pure JSF components/attributes instead.
Instead of c:forEach, rather use Seam's a4j:repeat or Facelets' ui:repeat and instead of c:if make use of the rendered attribute of the JSF component which has to be toggled to show/hide. Instead of all that JSTL c:set, write appropriate code logic in managed bean constructor or action method or getter.
The JSTL functions (fn) taglib is however still highly valuable in JSF. You can keep using it.
Related
I have a JSTL loop where I'm trying to check to see if a given variable is empty or not with a dynamic variable name. When I use c:set with page scope, the variable is not accessible to the if statement. However, when I set it using <% pageCotnext.setAttribute(...); %>, the variable is available.
<%
pageContext.setAttribute("alphaParA", "test");
pageContext.setAttribute("alphaParF", "test");
int i = 0;
%>
<ul class="alphadex_links">
<c:forEach var="i" begin="0" end="25" step="1" varStatus="status">
<c:set var="currentLetter" scope="page">&#${i+65}</c:set>
<c:set var="currentPar" scope="page">alphaPar${currentLetter}</c:set>
<% pageContext.setAttribute("currentPar", "alphaPar" + (char)('A' + i++)); %>
<li>
<c:choose>
<c:when test="${not empty pageScope[currentPar]}">
The test is always fails when I remove the pageContext.setAttribute block, however it succeeds for A and F as it should when the block is in. I'm very confused and can't find help anywhere.
It fails because HTML doesn't run at the moment JSTL runs. You're effectively passing a Java String A to it instead of the desired character A which would be represented as such based on the HTML entity A when the HTML is retrieved and parsed by the webbrowser after Java/JSP/JSTL has done its job. Please note that your HTML entity is missing the closing semicolon, but this isn't the cause of your concrete problem.
As to the concrete functional requirement, sorry, you're out of luck with EL. It doesn't support char. Your best bet is to deal with strings like this:
<c:forEach items="${fn:split('A,B,C,D,E,F,G,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z', ',')}" var="currentLetter">
<c:set var="currentPar" value="alphaPar${currentLetter}" />
${pageScope[currentPar]}
</c:forEach>
If necessary, just autogenerate the letters as String[] in Java end and set it as application attribute.
I am using this code in JSF.:
<c:if test="#{sV.done.booleanValue()}">
<option value="#{sV.id}" selected="selected">#{sV.text}</option>
</c:if>
<c:if test="#{not sV.done.booleanValue()}">
<option value="#{sV.id}">#{sV.text}</option>
</c:if>
sv is my class containing data (pojo), done is an Boolean variable, I want to display option tag with selected attribute if sV.done is true.
But I couldn't make it. Don't know where I am wrong.
Otherwise there can be something wrong with c, because c:forEach was not working before some time in my case in same page? It can be the reason? Where I am wrong?
Every time it displays option tag without selected attribute.
Try this: -
<c:if test="${sV.done == true}">...</c:if> // or
<c:if test="${sV.done eq true}">...</c:if> // or
<c:if test="${sV.done}">...</c:if> // or
And for negation (If sV.done is false): -
<c:if test="${! sV.done}">...</c:if> /// OR
<c:if test="${not sV.done}">...</c:if> /// OR
<c:if test = "${sV.done != true}">...</c:if> // OR
<c:if test = "${sV.done ne true}">...</c:if> // OR
For more on if with operators check out this link: - JSTL if
Your syntax is fine, provided that you're using EL 2.2. So, none of the JSTL <c:xxx> tags are been interpreted? You need to import the JSTL core taglib. It's unclear what view technology and JSTL version you're using, so here are import examples for both JSP and Facelets.
JSP with JSTL 1.0:
<%#taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
JSP with JSTL 1.1/1.2:
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
Facelets 1.x with JSTL 1.1/1.2:
<html ... xmlns:c="http://java.sun.com/jstl/core">
Facelets 2.x with JSTL 1.2:
<html ... xmlns:c="http://java.sun.com/jsp/jstl/core">
See also:
Our JSTL wiki page
Unrelated to the concrete problem, have you considered using a JSF UISelectOne component instead of fiddling with <option> elements yourself? You can find some concrete examples in our h:selectOneMenu wiki page.
JSF and JSTL both access 'boxed' objects (Boolean, Integer etc) directly. No unboxing is necessary -- so you don't have to call booleanValue().
<c:if test="${sV.done}">...</c:if>
But actually, your whole approach could be better -- I don't render a options in a page, without a list of options & a value. I don't write out loops & selection tests manually every time, there are tags or you can write a method to do this.
Super hint: I have a class called Pair( String name, Object value) & library method HtmlUI.renderSelectOptions(), to output SELECT options from a list of these & a 'current' value.
Select combo-box may not really be the best representation for a boolean either? You could go with a checkbox.. But this is up to your UI design.
If you did want to go this way, you could switch just the SELECTED attribute inside the tag. Easier than duplicating code for the entire , key & value.. For legacy code, I have a function that fulfills this exact requirement also :)
Hope this helps! Vote me up.
As I mentioned in question, It seems to look like some other component/library is interfering in work of c:if, it didn't work in any case. I tried all the suggestions given above. Thanks to all for replies.
I'm trying to change an JSF 1.1 page to conditionally hide parts of the page. The page is built using intermixed raw HTML and tags. Specifically I have the following:
<table>
<tr>
<td>Foo</td>
<td><h:inputText ... /></td>
</tr>
<tr>
<td>Bar</td>
<td><h:inputText ... /></td>
</tr>
<!-- more stuff, including <h:dataTable>
</table>
I would like to wrap this in a tag that conditionally hides this entire table, but I cannot seem to figure it out. Here's what I've tried:
Wrap the markup in a <h:panelGroup rendered="...">. While this correct shows/hides the markup, all raw the HTML is stripped from the generated HTML.
Wrap the markup in a <f:verbatim>. This does not work as the verbatim tag does not have a rendered attribute in JSF 1.1
Wrap the whole thing in a <h:panelGroup rendered="..."><f:verbatim> combo. This has the same effect as the first attempt.
I've also tried <f:view> and <f:subview> to no avail.
I know that it is possible to include JSTL tags in a JSF page and use <c:if> but I would like to avoid this situation. Any ideas?
NOTE: I realize that it is (by some at least) considered bad practice to mix HTML and JSF, however this page was created by someone else, I just have to modify it (its a somewhat large page and the HTML above is just a small snippet from it)..
Either replace <table> by <h:panelGrid>.
<h:panelGrid columns="2">
<h:outputText value="Foo" />
<h:inputText ... />
<h:outputText value="Bar" />
<h:inputText ... />
<!-- more stuff, including <h:dataTable>
</h:panelGrid>
Or make use of CSS display:none/block:
<table style="display: ${some condition ? 'none' : 'block'};">
Or just upgrade to JSF 1.2. Technically, a JSF 1.1 web application can easily be upgraded to JSF 1.2 without any code changes. It's only a matter of updating the JARs and changing the faces-config.xml root declaration to replace the JSF 1.1 DTD by a JSF 1.2 XSD. JSF 1.2 comes with an improved view handler which kills the <f:verbatim> nightmare (i.e. it is not needed anymore). It also comes with many, many bugfixes and performance enhancements you'd be very thankful for.
Unrelated to the concrete problem, as to your statement that mixing HTML and JSF is a bad practice, this isn't necessarily true. At least not since JSF 1.2 anymore. On JSF 1.0/1.1 you'd need to use <f:verbatim> which is in turn indeed a pain to develop/maintain. This caused the wrong myth that mixing JSF/HTML is "bad". See also What are the main disadvantages of Java Server Faces 2.0? for a bit of history on that.
I have a #ViewScoped bean with a List<String> containing plain HTML. I want to iterate over this list and output plain html:
<c:forEach items="#{bean.list}" var="html">
<f:verbatim>#{html}</f:verbatim>
</c:forEach>
That snippet above works well but when the page is refreshed the bean costrunctor is recalled. This issue/bug is known: JSTL c:forEach causes #ViewScoped bean to invoke #PostConstruct on every request
So the suggestion is to replace <c:forEach> with <ui:repeat>.
<ui:repeat value="#{bean.list}" var="html">
<f:verbatim>#{html}</f:verbatim>
</ui:repeat>
But that doesn't work. I have a blank page. I tried <h:dataTable>, <a4j:repeat> and <rich:dataTable> but nothing to do.
Any solutions?
Use <h:outputText escape="false"> instead.
<ui:repeat value="#{bean.list}" var="html">
<h:outputText value="#{html}" escape="false" />
</ui:repeat>
The <f:verbatim> is an old JSP-oriented tag which was intented to be able to embed plain HTML among JSF components in JSF 1.0/1.1. Without it, all the plain HTML would otherwise be rendered before the JSF component tree. This unintuitive behaviour was fixed in JSF 1.2 which made the tag superfluous. In Facelets it's also superfluous and in Facelets 2.0 (for JSF 2.0) it's even deprecated. See also the introductory text of the tag documentation. Don't use it. If you want to render unescaped HTML, rather use the <h:outputText escape="false"> as suggested in the above example. If you want to render pieces of inline HTML conditionally, rather use <h:panelGroup> or <ui:fragment> instead.
Why not to use ViewScoped datamodel?
in JEE5 (JSF 1.2) - could be seam-managed #Factory("bean.list") List<> produceList()... It will be view-scoped (actually page-scoped).
and in JEE6 (JSF 2.0) you can use the same pattern with CDI.
Or you can implement the same lifecycle for your list and create own solution.
I have this piece of code:
<c:if test="#{utils.getCounterOfCharOccurence(hideTypes, ';') != 0}">
<ui:repeat value="#{document.instanceList}" var="instance">
<c:set var="columnRendered" value="true"></c:set>
<c:forEach items="${hideTypes.split(';')}"
var="hideType">
<h:outputText value="#{hideType eq instance.documentInstanceType.mimeType}"/>
<c:if test="#{hideType eq instance.documentInstanceType.mimeType}">
<c:set var="columnRendered" value="false"></c:set>
<h:outputText value="#{columnRendered}|"/>
</c:if>
</c:forEach>
<a:outputPanel rendered="#{columnRendered == 'true'}">
<up:mimeTypeIcon type="#{instance.documentInstanceType.mimeType}"
icon="#{instance.documentInstanceType.iconPath}"
key="#{instance.instanceKey}" referenced="false"/>
</a:outputPanel>
</ui:repeat>
</c:if>
As you see, i render that outputPanel only when columnRendered is true.
Well, there are situations when this (used only for tests to approve what it should do):
<h:outputText value="#{hideType eq instance.documentInstanceType.mimeType}"/>
is true so it should enter in c:if and switch columnRendered to false. But it doesn't, so columnRendered is true forever...
Do you know why?
JSF and JSTL doesn't run in sync as you'd expect from the coding. JSTL runs during build time of the view (when the JSF component tree is to be populated) and JSF runs during render time of the view component tree (when HTML output is to be generated). You can visualize it as follows: JSTL runs first from top to bottom and then hands over the result to JSF which in turn runs from top to bottom again.
In your particular case, the object instance is never present in JSTL.
Instead of c:forEach, you should use ui:repeat and instead of c:if you should use JSF component's rendered attribute. I'd like to give a rewrite of the code, but the usage of hideTypes is a mess. Rather convert it to a List<String> in the model and it'll be much easier to do with pure JSF. Here's a kickoff example assuming that hideTypes is a List<String>:
<h:panelGroup rendered="#{not empty hideTypes}">
<ui:repeat value="#{document.instanceList}" var="instance">
<a:outputPanel rendered="#{!hideTypes.contains(instance.documentInstanceType.mimeType)}">
<up:mimeTypeIcon type="#{instance.documentInstanceType.mimeType}"
icon="#{instance.documentInstanceType.iconPath}"
key="#{instance.instanceKey}" referenced="false"/>
</a:outputPanel>
</ui:repeat>
<h:panelGroup>