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

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>

Related

Dynamic id for primefaces datatable [duplicate]

I'm trying to assign an id to a component inside a <ui:repeat> like that:
<ui:repeat value="#{bean.columns}" var="column">
<h:panelGroup layout="block" id="column_#{column.id}"
styleClass="#{column.id} dashboard_column">
The thing is that #{column.id} value is being placed properly inside the styleClass value but its not being set inside the id attribute. All that is being set inside the id attribute is the automatically generated id by the JSF + my hard coded value column_.
If I remove the hard coded column_ I get an exception:
java.lang.IllegalArgumentException: component identifier must not be a zero-length String
at
Any Ideas?
This is not possible with a render-time tag such as <ui:repeat>. The <ui:repeat> will however by itself already ensure the uniqueness of the generated client ID by prepending it with the row index. So just remove the EL part from the ID attribute of the component.
<ui:repeat value="#{bean.columns}" var="column">
<h:panelGroup layout="block" id="column">
With a view build time tag such as <c:forEach> (which will basically generate multiple <h:panelGroup> components instead of only one which is rendered multiple times), it is possible to specify a dynamic ID like that.
<c:forEach items="#{bean.columns}" var="column">
<h:panelGroup layout="block" id="column_#{column.id}">
(you should only be well aware of how JSTL works in Facelets)
An alternative is to use a static <div> element instead of a JSF <h:panelGroup layout="block"> component.
<ui:repeat value="#{bean.columns}" var="column">
<div id="column_#{column.id}">
See also:
JSTL in JSF2 Facelets... makes sense?
JSF prefixes the id automatically. If you simply write id="column" the generated HTML will contain such identifiers:
myForm:0:column
myForm:1:column
myForm:2:column
and so on.
Anyway: Do never use JSTL tags (like c:foreach and c:if) in JSF templates. They cause random behaviour, very difficult to debug. And if they work, the slow down the application a lot.
Use ui:repeat for loops, and ui:fragment for conditional blocks. Note that there is no replacement for c:set, such a construct does not exist anymore in JSF 2.

c:forEach or ui:repeat inside h:dataTable [duplicate]

I have to dinamically produce a list of tables. Each of these have a variable number of columns (with fixed rows).
To perform this I first put a <p:panelGrid> inside an <ui:repeat>: so I correctly produced a list of tables.
Then, to dinamically produce the columns, I tried put both an <ui:repeat> or a <c:forEach> inside the <p:panelGrid>. In the result I obtain no rows.
I written a minimal example here. In the bean testBackingBean, I have defined (and initialized) the variable ArrayList<ArrayList<String>> tables. This is the xhtml that does not produce the expected results:
<ui:repeat var="table" value="#{testBackingBean.tables}">
<p:panelGrid>
<f:facet name="header">
<p:row>
<p:column >header of #{table}</p:column>
</p:row>
</f:facet>
<p:row>
<c:forEach var="row" items="${table}">
<p:column>#{row}</p:column>
</c:forEach>
</p:row>
</p:panelGrid>
</ui:repeat>
Noteworthy the header-row correctly converts #{table} into string. The problem is that I see no rows of data.
Also, if I use the <table> instead of the <p:panelGrid> everything work as excpected.
Also, I tried different permutations of <c:forEach> and <ui:repeat> with no succes.
So, how can I dinamically produce more tables (using prime-faces) and set a dinamical number of columns?
Thanks!
EDIT: I would like to use two <c:forEach>, but even with one only <c:forEach> I get an empty result. In fact if I try the following xhtml:
<c:forEach items="${testBackingBean.tables}" var="tabella">
current element: #{tabella}
</c:forEach>
then I get an empty result. (I know,this is a different question)
The transition from the XHTML source code to the generated HTML output is a two-step process.
First, during view build time, the XHTML source code is parsed and turned in a tree of Java UIComponent instances representing the JSF UI component tree, as available by FacesContext#getViewRoot().
Then, during view render time, the JSF UI component tree produces HTML output and writes it to the HTTP resopnse, starting with UIViewRoot#encodeAll() method.
Taghandlers like all JSTL <c:xxx> tags, several JSF <f:xxx> tags and only a few Facelets <ui:xxx> tags run during view build time. UI components like all JSF <h:xxx> tags, several Facelets <ui:xxx> tags and only a few JSF <f:xxx> tags run during view render time.
The <c:forEach> is a taghandler and the <ui:repeat> is an UI component.
In other words, the UI components which are declared inside <c:forEach> are recreated multiple times in the JSF component tree based on <c:forEach items> during view build time which in turn individually produce each their own HTML output during view render time. The UI components which are declared inside <ui:repeat> are created only once in the JSF component tree during view build time which in turn are reused multiple times based on <ui:repeat value> to produce HTML output during view render time.
Your concrete problem is caused by the fact that <ui:repeat var="table"> is only available during view render time, not during view build time. The <c:forEach> is basically retrieving a #{null} as value when it's about to run during view build time.
You can solve this by replacing the outer <ui:repeat> by <c:forEach>. Although I wonder if you couldn't better use <ui:repeat><p:dataTable><p:columns> instead.
See also:
JSTL in JSF2 Facelets... makes sense?

Dynamically generate h:column based on list of hashmaps

In my application I want to display a <h:dataTable> with managed bean properties. Currently this table is created from a List<Folder>. Now I want to change the Folder to something more dynamic. That's because I don't want to change the Folder class if I decide to add another field later. I would just have to add another entry in the Map<String, Object> instead of introducing a new field in Folder.
So, is it possible to bind a List<Map<String, Object>> to the <h:dataTable>?
Is it possible to bind a List of HashMaps to the jsf component h:dataTable?
That's only possible if you generate the necessary <h:column> tags with a view build time tag such as JSTL <c:forEach>.
Here's a concrete kickoff example, assuming that your environment supports EL 2.2:
<h:dataTable value="#{bean.listOfMaps}" var="map">
<c:forEach items="#{bean.listOfMaps[0].keySet().toArray()}" var="key">
<h:column>
#{map[key]}
</h:column>
</c:forEach>
</h:dataTable>
(if your environment doesn't support EL 2.2, you'd need to provide another getter which returns the map key set as a String[] or List<String>; also keep in mind that a HashMap is by nature unordered, you might want to use LinkedHashMap instead to maintain insertion order)
When you're using Mojarra version older than 2.1.18, the disadvantage is that the #{bean} has to be request scoped (not view scoped). Or at least, the <c:forEach items> should refer a request scoped bean. A view scoped bean would otherwise be recreated on every single HTTP request as the <c:forEach> runs during view build time, when the view scope isn't availabe yet. If you absolutely need a view scoped bean for the <h:dataTable>, then you can always create a separate request scoped bean exclusively for <c:forEach items>. The solution would be to upgrade to Mojarra 2.1.18 or newer. For some background information, see also JSTL in JSF2 Facelets... makes sense?
JSF component libraries such as PrimeFaces may offer a <x:columns> tag which makes this more easy, such as <p:dataTable> with <p:columns>.
<p:dataTable value="#{bean.listOfMaps}" var="map">
<p:columns value="#{bean.listOfMaps[0].keySet().toArray()}" var="key">
#{map[key]}
</p:columns>
</p:dataTable>

JSF Primefaces TabView problems

I asked this in the PF Forum but no one seems to want to answer so I though I'd try my luck here.
I have a ui:repeat that is not being updated correctly after an Ajax call when it is within a TabView.
Simple scenario is I have a ui:repeat pointing at an ArrayList (ArrayList contains simple pojos with a String). Within this I have an h:inputText whose value is the pojo's String getter/setter. The ui:repeat is contained within a h:panelGroup. I use a p:commandButton to run an action to update the ArrayList (just add a couple of objects to it a Math.random value for the String) and then update the h:panelGroup. The updated values in the ArrayList are not reflecting in the ui:repeat input fields. This only appears to be affecting input fields as outputText fields do update correctly. Also if I do the same for a p:dataTable the input field are updated correctly. If I remove the Tabview and Tab tags it works fine.
As it works when removing the Tabs I can only assume this is a bug and not designed to work like this. If someone could please confirm if this is so or if there is a viable work around. I need to use a ui:repeat as my fields are not in a tabular format. This has only occurred since migrating from PF 2.2. I'm currently on PF 3.1, Weblogic 10.3.4 and Mojarra 2.0.4
<p:tabView>
<p:tab title="Test">
<h:form prependId="false">
<p:commandButton id="testStringCheck"
value="Test String Check"
process="#form"
update="testPanel"
action="#{testBean.generateVOwithRandomStrings}">
</p:commandButton>
<h:panelGroup id="testPanel" layout="block">
<ui:repeat value="#{testBean.voList}" var="entry">
<h:outputText value="#{entry.randomString}"/>
<p:inputText style="display:block;"
value="#{entry.randomString}"
size="15">
</p:inputText>
</ui:repeat>
</h:panelGroup>
</h:form>
</p:tab>
</p:tabView>
As a workaround I've used a p:datagrid instead of a ui:repeat. This achieves the look I had in the the ui:repeat so I'm happy. Hopefully this bug will be fixed on future releases.
This is something of a bug in Primefaces commandButton. See the following thread:
http://forum.primefaces.org/viewtopic.php?f=3&t=17454
You can try replacing
<p:commandLink id="testStringCheck" ... update="#form" />
With an <h:commandLink>
<h:commandLink id="testStringCheck" render="#form"/>
Also from the above link here is an interesting method that somebody posted that enables you to correctly find the correct clientId to update.
http://paste.kde.org/177698/
temp solution: insert outside h:panelGroup:
<p:outputPanel defered="true" delay="1" ..>
and update outputPanel instead of panelGroup
Or use a datalist component instead of ui:repeat.
One more, i'm not sure but you can try, so update class instead id:
<h:panelGroup id="testPanel" layout="block" styleClass="testPanelCl" ../>
and update : #(.testPanelCl), don't forget id of panelGroup, without id JSF can not update by class

ui:repeat and h:panelGrid

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?

Resources