ui:repeat and h:panelGrid - jsf

When using something like
<h:panelGrid columns="1">
<ui:repeat var="o" value="#{mybean.list}">
<h:outputText value="#{o.text}"/>
</ui:repeat>
</h:panelGrid>
with lets say 10 list entries I only get 1 row e.g.: one tr with 1 td whereas when I use c:forEach i get 10 (but c:forEach is in fact evil, it messes up everything with ajax)
I use mojarra 1.2 - is this a typical Mojarra bug which does not exist in the MyFaces implementation? Will it disappear in 2.x of the Mojarra releases?

The output is fully as expected and specified. The <ui:repeat> is a render time tag, not a view build time tag like <c:forEach>. After building the view, <h:panelGrid> ends up with 1 child component (the <ui:repeat> itself), not with n <h:outputText> components like as with <c:forEach>.
You need a <h:dataTable> instead. It's designed for exactly this purpose.
<h:dataTable var="o" value="#{mybean.list}">
<h:column>
<h:outputText value="#{o.text}"/>
</h:column>
</h:dataTable>
See also:
JSTL in JSF2 Facelets... makes sense?

Related

Why can <c:forEach> or <ui:repeat> not access <p:dataTable var>?

I am working with jsf Mojarra 2.2.7, Java 8, Primefaces 5.1 and netbeans 8.0.2
I have a class Event with a property List<GameRecord> gameRecordList. GameRecord includes List<Boolean> gamesEntered and other properties. The idea is I have a list of people in an event and am configuring if they are entered into bets or competitions.
In my .xhtml file I have
<p:dataTable value="#{events.gameRecordList}" var="item" rowIndexVar="rowIndex">
<p:column>#{item.field1}</p:column>
<p:column>#{item.field2}</p:column>
<c:forEach items="#{events.gameRecordList.get(rowIndex).gamesEntered}" var="game">
<p:column>
<p:selectBooleanCheckbox value="#{game}"/>
</p:column>
</c:forEach>
</p:dataTable>
The <c:forEach> should work with value="#{item.gamesEntered}" rather than the full string but it does not. I have tried <ui:repeat> but either way the page comes up blank where this data should have appeared.
Does this make sense or is there a reason the full addressing is required to make it work?
The <c:forEach> should work with value="#{item.gamesEntered}" rather than the full string but it does not.
JSTL tags run during view build time, building JSF component tree. JSF components run during view render time, producing HTML output. So at the moment <c:forEach> runs, <p:dataTable> hasn't run and its var is nowhere available and will evaluate as null. Note that the same applies to rowIndexVar, which will evaluate as 0 (the default value of an int).
See also
JSTL in JSF2 Facelets... makes sense?
I have tried <ui:repeat> but either way the page comes up blank where this data should have appeared.
UIData components can only accept UIColumn children. The <ui:repeat> isn't such one. The <c:forEach> works because it basically produces a bunch of physical <p:column> components for the datatable. You're lucky that each item has apparently the same amount of gamesEntered as the first item, this would otherwise have failed hard as well.
See also:
How to use ui:repeat in datatable to append columns?
By the way, you need <p:columns> which is basically an <ui:repeat> which extends from UIColumn class. But also here, its value cannot be set on a per-row basis, only on a per-table basis. The rowIndexVar isn't available in <p:columns value> and would evaluate as 0 anyway.
<p:dataTable value="#{events.gameRecordList}" var="item" rowIndexVar="rowIndex">
<p:column>#{item.field1}</p:column>
<p:column>#{item.field2}</p:column>
<p:columns value="#{events.gameRecordList[0].gamesEntered}" columnIndexVar="columnIndex">
<p:selectBooleanCheckbox value="#{events.gameRecordList[rowIndex].gamesEntered[columnIndex]}"/>
</p:columns>
</p:dataTable>
See also:
Primefaces static and dynamic columns in datatable
Dynamic columns with List<List> in <p:dataTable><p:columns>

Trying to render a h:panelGroup outside ui:repeat with complicated structure

This is the general structure of the jsf page:
<ui:repeat id="repeater" varStatus="stat" >
<h:form id="the-form">
<h:panelGroup id="renderme">
<ui:repeat id="inner-repeater">
<h:commandButton>
<f:ajax render=":repeater:#{stat.index}:the-form:renderme">
</h:commandButton>
</ui:repeat>
</h:panelGroup>
</h:form>
</ui:repeat>
So.. the button here, when clicked, should cause the element renderme to be re-rendered.
In practice, I get "component with id ... not found" although when I look at the html page, the generated id is correct.
Moreover, I tried to use #{component.parent.parent.clientId} which produced the same id and still got the same error message from JSF.
Any idea on why this is failing?
Thanks!
This doesn't work for the simple reason because viewRoot.findComponent("repeater:0:the-form:renderme") as requested by <f:ajax render> doesn't return anything. Such a component does not exist in the component tree. This ID only exists in the generated HTML output. Instead, it's viewRoot.findComponent("repeater:the-form:renderme") which would return something, but this does in turn not exist in HTML DOM tree where JavaScript needs to do the update based on ajax response. Even then, this is not exactly what you need.
This is where JSTL can come to rescue. It's capable of dynamically building the JSF component tree and of dynamically assigning IDs to components generated in a loop:
<c:forEach ... varStatus="stat">
<h:form id="the-form_#{stat.index}">
<h:panelGroup id="renderme">
<ui:repeat id="inner-repeater" ...>
<h:commandButton ...>
<f:ajax render=":the-form_#{stat.index}:renderme">
</h:commandButton>
</ui:repeat>
</h:panelGroup>
</h:form>
</c:forEach>
This will only cause problems when you bind <c:forEach> value to a view scoped bean and you use Mojarra version older than 2.1.18. You'd need to upgrade to at least Mojarra 2.1.18 then.
See also:
JSTL in JSF2 Facelets... makes sense?

<c:foreach> tag not evaluating an object

I have the following piece of code:
<h:outputText value="#{lecture.lectureName}" />
<c:forEach items="#{criterionController.getCriteriaForLecture(lecture)}" var="criterion">
<h:outputText value="#{criterion.criterionName}" />
<h:commandLink value="Edit"/>
<h:commandLink value="Delete"/>
</c:forEach>
The output text part is working perfectly and displays what it should display so this proves that the lecture object is set. However the for each tag gives a null pointer exception. When I debugged the code, I saw that the lecture object was taken as null when the method getCriteriaForLecture() was called.
How can this behaviour explained?
This can happen if the lecturer variable is in turn been set by a JSF iterating component such as <h:dataTable>, <ui:repeat>, etc or probably a <p:tabView>, based on your previous question.
A more detailed explanation of this behaviour can be found here: JSTL in JSF2 Facelets... makes sense? To the point, JSTL tags runs during building the view, not during rendering the view. The lecturer variable is in your particular case only available during rendering the view and is thus always null during building the view, when JSTL runs.
To solve it, use a normal JSF component like <ui:repeat> instead.
<ui:repeat value="#{criterionController.getCriteriaForLecture(lecture)}" var="criterion">
<h:outputText value="#{criterion.criterionName}" />
<h:commandLink value="Edit"/>
<h:commandLink value="Delete"/>
</ui:repeat>
Much better would be not doing business actions in getters at all. Just make the List<Criterion> a property of Lecture instead.
<ui:repeat value="#{lecture.criterions}" var="criterion">
<h:outputText value="#{criterion.criterionName}" />
<h:commandLink value="Edit"/>
<h:commandLink value="Delete"/>
</ui:repeat>
See also Why JSF calls getters multiple times

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>

JSTL c:if doesn't work inside a JSF h:dataTable

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

Resources