How to Iterate Over java.util.Set in JSF
I spent quite some time trying to find a solution for the following JSF issue: it is not possible to iterate over a java.util.Set.
– ui:repeat (facelets) doesn’t work
– a4j:repeat (richfaces) doesn’t work
– c:forEach works..only in case it does not rely on a variable defined by a parent component (rich:dataTable for instance)
All above are pretty logical phenomena, as UIData relies on ordered data, and generally a Set is not ordered.
In my case I had to use a Set defined in the Hibernate (JPA) object (PersistentSet).
An important note: you should use a set in case the view order is of no matter to you.
The solution..is pretty simple. And I’ll suggest it to be a part of facelets/richfaces for the next version, unless of course there is some valid specific reason for it not to be.
1. Define your own UI component extending an existing repeater component. I used a4j:repeat (HtmlAjaxRepeat)
2. Override the metohd getDataModel
3. Define your component in your faces-config
4. create a custom facelets tag definition
5. Define a context-variable in web.xml pointing to the facelet tag definition.
Note: for use with JSP instead of Facelets, you should define a .tld and a Tag handler, which is not an ojbect of this post.
Now let’s see the steps in detail:
1,2. Here some code:
package com.myproject.components;
import java.util.ArrayList;
import java.util.Set;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import org.ajax4jsf.component.html.HtmlAjaxRepeat;
import org.ajax4jsf.model.SequenceDataModel;
public class UIIterator extends HtmlAjaxRepeat {
@SuppressWarnings("unchecked")
@Override
protected DataModel getDataModel() {
Object current = getValue();
if(current instanceof Set){
return new SequenceDataModel(new ListDataModel(
new ArrayList((Set) current)));
}
return super.getDataModel();
}
}
So, as we don’t care about the order of the elements, we just create a new ArrayList out of the Set. And we can now easily return the appropirate DataModel.
3. Add this to your faces-config. (I copied it from the a4j definition)
<component>
<description />
<display-name>Iterator</display-name>
<component-type>com.myproject.Iterator</component-type>
<component-class>com.myproject.components.UIIterator</component-class>
<component-extension>
<component-family>javax.faces.Data</component-family>
<renderer-type>org.ajax4jsf.components.RepeatRenderera</renderer-type>
</component-extension>
</component>
4. Here is the tag definition for facelets
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE facelet-taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
"http://java.sun.com/dtd/facelet-taglib_1_0.dtd">
<facelet-taglib xmlns="http://java.sun.com/JSF/Facelet">
<namespace>http://myproject.com/cust</namespace>
<tag>
<tag-name>repeat</tag-name>
<component>
<component-type>com.myproject.Iterator</component-type>
<renderer-type>org.ajax4jsf.components.RepeatRenderer</renderer-type>
</component>
</tag>
</facelet-taglib>
Save this file as /WEB-INF/facelets/custom.taglib.xml
5. Add to your web.xml
<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>/WEB-INF/facelets/custom.taglib.xml</param-value>
</context-param>
6. It is now ready to use
...
xmlns:cust="http://myproject.com/cust"
...
<cust:repeat var="myVar" value="${aSet}">
...
</cust:repeat>
I think it is way neater than other workarounds, like defining a custom EL Resolver.
I spent quite some time trying to find a solution for the following JSF issue: it is not possible to iterate over a java.util.Set.
– ui:repeat (facelets) doesn’t work
– a4j:repeat (richfaces) doesn’t work
– c:forEach works..only in case it does not rely on a variable defined by a parent component (rich:dataTable for instance)
All above are pretty logical phenomena, as UIData relies on ordered data, and generally a Set is not ordered.
In my case I had to use a Set defined in the Hibernate (JPA) object (PersistentSet).
An important note: you should use a set in case the view order is of no matter to you.
The solution..is pretty simple. And I’ll suggest it to be a part of facelets/richfaces for the next version, unless of course there is some valid specific reason for it not to be.
1. Define your own UI component extending an existing repeater component. I used a4j:repeat (HtmlAjaxRepeat)
2. Override the metohd getDataModel
3. Define your component in your faces-config
4. create a custom facelets tag definition
5. Define a context-variable in web.xml pointing to the facelet tag definition.
Note: for use with JSP instead of Facelets, you should define a .tld and a Tag handler, which is not an ojbect of this post.
Now let’s see the steps in detail:
1,2. Here some code:
package com.myproject.components; import java.util.ArrayList; import java.util.Set; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; import org.ajax4jsf.component.html.HtmlAjaxRepeat; import org.ajax4jsf.model.SequenceDataModel; public class UIIterator extends HtmlAjaxRepeat { @SuppressWarnings("unchecked") @Override protected DataModel getDataModel() { Object current = getValue(); if(current instanceof Set){ return new SequenceDataModel(new ListDataModel( new ArrayList((Set) current))); } return super.getDataModel(); } }
So, as we don’t care about the order of the elements, we just create a new ArrayList out of the Set. And we can now easily return the appropirate DataModel.
3. Add this to your faces-config. (I copied it from the a4j definition)
<component> <description /> <display-name>Iterator</display-name> <component-type>com.myproject.Iterator</component-type> <component-class>com.myproject.components.UIIterator</component-class> <component-extension> <component-family>javax.faces.Data</component-family> <renderer-type>org.ajax4jsf.components.RepeatRenderera</renderer-type> </component-extension> </component>
4. Here is the tag definition for facelets
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "http://java.sun.com/dtd/facelet-taglib_1_0.dtd"> <facelet-taglib xmlns="http://java.sun.com/JSF/Facelet"> <namespace>http://myproject.com/cust</namespace> <tag> <tag-name>repeat</tag-name> <component> <component-type>com.myproject.Iterator</component-type> <renderer-type>org.ajax4jsf.components.RepeatRenderer</renderer-type> </component> </tag> </facelet-taglib>
Save this file as /WEB-INF/facelets/custom.taglib.xml
5. Add to your web.xml
<context-param> <param-name>facelets.LIBRARIES</param-name> <param-value>/WEB-INF/facelets/custom.taglib.xml</param-value> </context-param>
6. It is now ready to use
... xmlns:cust="http://myproject.com/cust" ... <cust:repeat var="myVar" value="${aSet}"> ... </cust:repeat>
I think it is way neater than other workarounds, like defining a custom EL Resolver.
This is very hot info. I’ll share it on Twitter.
After reading the article, I feel that I need more information on the topic. Can you suggest some resources please?
Thanks Bozho. It works perfectly. I’ll also share it on Twitter.
Hi,
I tried your approach, but I do not get the values rendered inside the new component. Any idea?
No, at least until provide more data on the exact issue 😉
Hi,
yes sorry but my code snippet was not posted:
I implemented exactly the same you did here. No I integrated this into a JSF page and I have the following:
I have a user bean containing several roles (this is the set) and I use rich:dataList for iterating over the users. Now inside the rich:datalist I use the cust:repeat using the roles attribute as value. Now I am using a h:outputText for printing out the var: myVar and I do not get any data on the screen. But the data is inside the loaded object.
If you want to use the iteration on rich:dataList, you must extend HtmlDataList instead of HtmlAjaxRepeat
There’s an easier way.
(the code wasn’t send)
There’s an easier way.
c:forEach var=”element_” items=”#{anyObject.elements}”
c:set var=”element” value=”#{element_}” scope=”request”/
h:outputText value=”#{element.description} “/
/c:forEach
Hi !
I found another solution (and a little bit easier 😉 )
http://sfjsf.blogspot.com/2006/03/usings-sets-with-uidata.html
regards!
Hi Bozho.
Unfortunately com.sun.facelets.component.UIRepeat has method getDataModel() as private.
Your tag handler code could be made more generic to accept not only Set, but all of Collection (I also added a type so you can drop the @SuppressWarnings annotation):
———————————————————-
@Override
protected DataModel getDataModel() {
Object current = getValue();
if (current instanceof Collection && !(current instanceof List)) {
return new SequenceDataModel(
new ListDataModel(
new ArrayList(
(Collection) current
)
)
);
}
return super.getDataModel();
}
———————————————————-
The generic type parameters were scrubbed from my previous post. The ArrayList should be of type “Object” and the Collection should be “?”.
Hi Bozho,
Congratulations for the great article. In RichFaces 4 the class of the a4j:repeat component is org.richfaces.component.UIRepeat, which hasn’t got method getDataModel(). Is there any new solution for RichFaces 4?
I don’t know, haven’t used RichFaces 4
How strange that I find a 3 year old article and about something I need to solve today, and the writer has actually commented on it only yesterday! Incredible.
I’m trying to do this with a datable which component will I need to extend?
Hi Glen, it depends on which datatable you want to use, but components are usually called UIXX. For example: UIDataTable.
But if it’s used in only a couple of places, you can make a new List based on the Set in your managed bean and pass that to the datatable.
HI, with Struts nested tag, we can edit the content of collection and send back to action. How can we do this in JSF. Can any one help me Please.
This is the perfect webpage for anybody who
hopes to understand this topic. You know so much
its almost tough to argue with you (not that I personally will need to…HaHa).
You definitely put a new spin on a topic that
has been discussed for years. Great stuff,
just excellent!
Hello colleagues, how is all, and what you
would like to say regarding this piece of writing, in my view its in fact
amazing in favor of me.
Everyone loves what you guys tend to be up too. This type of clever work and exposure!
Keep up the great works guys I’ve incorporated you guys to blogroll.
Spot on with this write-up, I seriously feel this amazing site
needs much more attention. I’ll probably be back again to see more, thanks for the info!
This paragraph provides clear idea in favor of the new visitors of blogging,
that really how to do blogging and site-building.
This post will help the internet people for setting up new webpage
or even a weblog from start to end.
A motivating discussion is worth comment. I think that you ought to write more on this subject
matter, it might not be a taboo subject but usually people do not talk about these issues.
To the next! Best wishes!!
Tremendous things here. I’m very happy to peer your post.
Thank you a lot and I am taking a look forward to touch you.
Will you kindly drop me a mail?
I’m not sure the place you’re getting your information, but
great topic. I must spend a while studying much more or understanding more.
Thanks for great info I used to be in search of this info for my mission.