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?
Related
I'm trying to use <c:if> to conditionally put a <h:outputLink> inside a <h:dataTable> when the state is finished.
<h:dataTable value="#{bean.items}" var="item" width="80%">
<h:column>
<f:facet name="header">
<h:outputText value="State" />
</f:facet>
<c:if test="#{item.state != 'Finish'}">
<h:outputText value="Missing value" />
</c:if>
<c:if test="#{item.state == 'Finish'}">
<h:outputLink value="myLink">
<h:outputText value="Value = #{item.state}" />
</h:outputLink>
</c:if>
</h:column>
</h:dataTable>
But this does not work, why is that and how can I fix it?
JSTL tags are evaluated during building of the view, not during rendering of the view. You can visualize it as follows: Whenever a view tree get created for the first time, all JSTL tags are executed and the result is a view with only JSF components. Whenever a view tree get rendered, all JSF components get executed and the result is HTML. So: JSF+JSTL doesn't run in sync as you'd expect from the coding. JSTL runs from top to bottom first, hands the result to JSF and then it's JSF's turn to run from top to bottom again. This may lead to unexpected results in JSF iterating components like UIData because the row data (in your particular case the #{item} object) is not available while JSTL runs.
In a nutshell: Use JSTL to control flow of JSF component tree building. Use JSF to control flow of HTML output generation.
You want to use the rendered attribute here.
<h:outputText value="Missing value" rendered="#{item.state ne 'Finish'}" />
<h:outputLink value="myLink" rendered="#{item.state eq 'Finish'}">
<h:outputText value="Value = #{item.state}" />
</h:outputLink>
See also:
JSTL in JSF2 Facelets... makes sense?
Conditionally displaying JSF components
In my JSF page I use <c:choose><c:when> tag to conditionally display content. However, it does not work as it seems to always evaluate false.
E.g.
<h:outputText value="#{pollInfo.active} -" />
<c:choose>
<c:when test="#{pollInfo.active}">
<h:outputText value="Active" />
</c:when>
<c:otherwise>
<h:outputText value="Deactive" />
</c:otherwise>
</c:choose>
There are for sure a few items with active=true, which is confirmed by <h:outputText>, but it just prints Deactive for all items. You can see the actual output in the following picture:
How is this caused and how can I solve it?
The symptoms and the screenshot suggests that the #{pollInfo} represents the currently iterated item of an iterating UI component such as <ui:repeat>, <h:dataTable>, <p:tabView>, <p:dataTable>, etc.
JSTL tags run during view build time, that moment when JSF component tree is built based on XHTML source code. The var attribute of such an iterating UI component is only available during view render time, that moment when HTML output is produced based on JSF component tree.
In other words, they don't run "in sync". The #{pollInfo} is always null during view build time.
In this particular case, you need the JSF component's rendered attribute instead.
<h:outputText value="Active" rendered="#{pollInfo.active}" />
<h:outputText value="Deactive" rendered="#{not pollInfo.active}" />
Or if you intend to conditionally render larger pieces of code, wrap all in a <ui:fragment>:
<ui:fragment rendered="#{pollInfo.active}">
<h:outputText value="Active" />
<!-- Some more components here if necessary. -->
</ui:fragment>
<ui:fragment rendered="#{not pollInfo.active}">
<h:outputText value="Deactive" />
<!-- Some more components here if necessary. -->
</ui:fragment>
Again another alternative, given that you've a pure if-else, is using the conditional operator in EL:
<h:outputText value="#{pollInfo.active ? 'Active' : 'Deactive'}" />
Additional bonus is, you end up with much less code.
See also:
JSTL in JSF2 Facelets... makes sense?
as the question above mentiones, i need to create "dynamic" params for a
<ui:composition>
<h:link>
<h:outputText value="link with params" />
<ui:repeat var="parameter" value="#{bean.getCurrentParameter}"> //customClass
test: #{parameter.name} #{parameter.value} //output is fine
<f:param name="#{parameter.name}" value="#{parameter.value}" />
</ui:repeat>
</h:link>
</ui:composition>
unfortunately the "test" returns all values correctly, but when I hover the link, there is not a single parameter set ("page.xhtml" instead of "page.xhtml?param1=ddd¶m2=sss...")
To unterstand why I need this, I want to get all parameters of the current page and add/remove one (the link clicked on is the one I want to remove/add).
to I need to generate for each link its own parameters (when param1=1,2 by default, one link has e.g. "param1=1,2,3" (appends 3) and the other one has "param1=1,2,4" (appends 4))
Classic taghandlers vs component tags issue. <ui:repeat/> is a component tag that runs after the view tree has been built while <f:param/> is a tag handler that is placed in the view tree during view build. What this means is that <f:param/> is parsed and processed before <ui:repeat/> ever makes it into the page. As a result var="parameter" is not available for use when <f:param/> needs it.
To fix, use the <c:forEach/> tag instead:
<h:link>
<h:outputText value="link with params" />
<c:forEach var="parameter" items="#{bean.getCurrentParameter}">
test: #{parameter.name} #{parameter.value}
<f:param name="#{parameter.name}" value="#{parameter.value}" />
</c:forEach>
</h:link>
Is it possible to have a JSF component that has ui:repeat and inside the repeat call the same component? It's because I'm building tree of question:
<cc:interface>
<cc:attribute name="questions" required="true" />
<cc:attribute name="renderQuestions" required="true" default="true" />
</cc:interface>
<cc:implementation>
<c:if test="#{cc.attrs.renderQuestions}">
<ui:repeat value="#{cc.attrs.questions}" var="q">
<p:panelGrid columns="2">
<h:outputLabel value="#{q.question}"></h:outputLabel>
<p:selectBooleanButton onLabel="#{messages['commons.yes']}"
offLabel="#{messages['commons.no']}" onIcon="ui-icon-check"
offIcon="ui-icon-close" value="#{q.ok}">
<p:ajax update="#all"></p:ajax>
</p:selectBooleanButton>
</p:panelGrid>
<cf:question renderQuestions="#{q.ok}" questions="#{q.children}" />
</ui:repeat>
</c:if>
</cc:implementation>
Currently I'm having stackoverflow?
Use a view build time tag to stop the recursion instead of a view render time tag. The component tree is built during view build time. The rendered attribute isn't evaluated during view build time, but instead during view render time. So your construct basically keeps including itself in an infinite loop. You should already know that StackOverflowError is caused by a recursion so deep that the memory stack can't handle it anymore (usually around ~1000 iterations).
Replace <h:panelGroup rendered> by a <c:if test> and it'll work as expected. The renderQuestions can by the way be simplified by omitting the check on children. The <ui:repeat> won't render anything anyway if there are no children.
If you happen to use view scoped beans in this construct and you're using Mojarra implementation, then make sure that you upgrade to at least 2.1.18, because binding a view build time tag attribute to a view scoped bean property would in older versions break the view scope.
See also:
JSTL in JSF2 Facelets... makes sense? - explains "view build time" versus "view render time"
Based on the answer of BalusC I created a solution like this.
<composite:interface>
[..]
<composite:attribute name="level" required="true" default="0"/>
</composite:interface>
<composite:implementation>
<c:if test="#{cc.attrs.level == 0}">
<a4j:repeat value="#{cc.attrs.list}" var="subList" rendered="#{cc.attrs.list != null}">
<xx:recursiveComponent list="#{subList}" id="subComponent" level="1"/>
</a4j:repeat>
</c:if>
</composite:implementation>
The level attribute is used at build time to avoid endless recursion and the renered attribute of the a4j:repeat (or ui:repeat in your case) will be evaluated at render time.
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>