在JSF和SEAM中对java.util.Set及其子类作循环

在JSF和SEAM中对java.util.Set及其子类作循环

How to iterate over java.util.Set in JSF

By bozhobg

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:

view plain copy to clipboard print ?
  1. package com.myproject.components;  
  2. import java.util.ArrayList;  
  3. import java.util.Set;  
  4.   
  5. import javax.faces.model.DataModel;  
  6. import javax.faces.model.ListDataModel;  
  7.   
  8. import org.ajax4jsf.component.html.HtmlAjaxRepeat;  
  9. import org.ajax4jsf.model.SequenceDataModel;  
  10.   
  11. public class UIIterator extends HtmlAjaxRepeat {  
  12.   
  13.    @SuppressWarnings("unchecked")  
  14.    @Override  
  15.    protected DataModel getDataModel() {  
  16.       Object current = getValue();  
  17.       if(current instanceof Set){  
  18.           return new SequenceDataModel(new ListDataModel(  
  19.                 new ArrayList((Set) current)));  
  20.       }  
  21.       return super.getDataModel();  
  22.    }  
  23. }  
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)

view plain copy to clipboard print ?
  1. <component>  
  2.         <description />  
  3.         <display-name>Iterator</display-name>  
  4.         <component-type>com.myproject.Iterator</component-type>  
  5.         <component-class>com.myproject.components.UIIterator</component-class>  
  6.   
  7.         <component-extension>  
  8.             <component-family>javax.faces.Data</component-family>  
  9.             <renderer-type>org.ajax4jsf.components.RepeatRenderera</renderer-type>  
  10.         </component-extension>  
  11.     </component>  
<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

view plain copy to clipboard print ?
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE facelet-taglib PUBLIC  
  3. "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"  
  4. "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">  
  5. <facelet-taglib xmlns="http://java.sun.com/JSF/Facelet">  
  6. <namespace>http://myproject.com/cust</namespace>  
  7.   
  8. <tag>  
  9. <tag-name>repeat</tag-name>  
  10. <component>  
  11. <component-type>com.myproject.Iterator</component-type>  
  12. <renderer-type>org.ajax4jsf.components.RepeatRenderer</renderer-type>  
  13. </component>  
  14. </tag>  
  15.   
  16. </facelet-taglib>  
<?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

view plain copy to clipboard print ?
  1. <context-param>  
  2. <param-name>facelets.LIBRARIES</param-name>  
  3. <param-value>/WEB-INF/facelets/custom.taglib.xml</param-value>  
  4. </context-param>  
<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.

你可能感兴趣的:(在JSF和SEAM中对java.util.Set及其子类作循环)