Creating SelectItem lists for dropdown and list controls is a pretty boring task. Everytime you do it, it’s like why isn’t this already part of JSF - it’s the same stupid task again and again.

So I was like - I should help myself and create something reusable, a component! I have been using JSF now for a while but only slowly I’m starting to write my own JSF components (creating a JSF component is a pretty heavy task, this should get easier in future releases of the JSF specification!).

This time it came to my mind that JSP allows to create EL functions that only require a class with a static method, a tag library descriptor and can be packaged easily into a JAR. So I’d wish a function that can be used in a JSF JSP to convert any collection like type such that JSF can display it through one of its selectOne, selectMany, etc. components.

1
2
3
4
5
6
  
<%@ taglib uri="http://at.martinahrer.blueprint.faces.el.si" prefix="si"%>

<tr:selectonechoice label="#{resources.label}" value="#{managedBean.property}">
   <tr:selectitems value="#{si:selectItems(managedBean.collection, 'item.label', 'item')}"/>
</tr:selectonechoice>

So the selectItem function would just do it’s job and no longer my presentation logic would get polluted with all this collection iterations and SelectItem instantiations. Sounds interesting, but what is required to implement this function? First we need a tag library descriptor that is dropped somewhere into the WEB-INF folder. And we need the Java code that is actually doing all the heavy job.

1
2
3
4
5
6
7
8
9
10
11
12
13
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee">
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd">
  <tlib-version>1.0.0</tlib-version>
  <short-name>si</short-name>
  <uri>http://at.martinahrer.blueprint.faces.el.si</uri>
  <function>
      <name>selectItems</name>
      <function-class>at.martinahrer.blueprint.faces.el.SelectItems</function-class>
      <function-signature>
          java.util.Map selectItems(java.lang.Object,java.lang.String,java.lang.String)
      </function-signature>
  </function>
</taglib>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class SelectItems {
  public static Map<string, Object> selectItems(Object value, String labelExpression, String variable) {
      assert (variable != null);

      Collection<object> items = null;
      if (value instanceof Collection) {
          items = (Collection<object>) value;
      } else if (value.getClass().isArray()) {
          items = Arrays.asList((Object[]) value);
      } else {
          throw new IllegalArgumentException(value.toString());
      }

      FacesContext currentInstance = FacesContext.getCurrentInstance();
      Map<string, Object> map = new HashMap<string, Object>(items.size());
      ValueBinding labelBinding = null;
      if (labelExpression != null) {
          labelBinding = currentInstance.getApplication().createValueBinding("#{" + labelExpression + "}");
      } else {
          labelBinding = currentInstance.getApplication().createValueBinding("#{" + variable + "}");
      }
      for (Object item : items) {
          currentInstance.getExternalContext().getRequestMap().put(variable, item);
          map.put(labelBinding.getValue(currentInstance).toString(), item);
          currentInstance.getExternalContext().getRequestMap().remove(variable);
      }
      return map;
  }
}

Well, while implementing this stuff I found several other solutions. JBoss Seam is providing some enhanced JSF controls like MyFaces. If you don’t want to pull in their libraries I guess my solution is pretty compact and easy to deploy as well!

Comments