Spring & JSF integration: Select Items
new SelectItem(Title.MISS, "Miss");
Working with SelectItems before JSF 2.0 was often tedious as you needed to write code to adapt your domain objects into SelectItems. JSF 2.0 has improved things a lot and you can now dynamically construct SelectItems using EL expressions. For example:
<h:selectOneMenu> <f:selectItems value="#{customerRepository.all}" var="customer" label="#{customer.name}"/> </h:selectOneMenu>
This certainly helps to reduce the amount of boiler-plate code, however, I still think that there are things that we can do make SelectItems even easier to use, especially when working with Spring. With that in mind, I have been developing a <s:selectItems> component, intended as a drop-in replacement for <f:selectItems>.
The first thing we can do, is help to reduce boiler-plate typing by removing the need to specify a var attribute. With <s:selectItems>, if the var attribute is not specified then it will default to item. So the code above can be written:
<h:selectOneMenu> <s:selectItems value="#{customerRepository.all}" label="#{item.name}"/> </h:selectOneMenu>
In the above example, the value is being bound to a repository Interface that returns a Collection of Customer entities. As with the standard <f:selectItems> components you can also bind to an Array or DataModel. In addition the new component also supports any comma separated String value.
<h:selectOneMenu> <s:selectItems value="Java, Spring, JavaServer Faces"/> </h:selectOneMenu>
The next thing that <s:selectItems> can help with is null values. It is quite common to need a “Please Select” option in drop downs to represent nulls. In vanilla JSF this can often mean additional mark-up for each component:
<h:selectOneMenu> <f:selectItem label="--- Please Select ---" noSelectionOption="true" itemValue=""/> <s:selectItems value="'{items}"/> </h:selectOneMenu>
Instead of needing this additional mark-up for each element, our component will automatically insert a “Please Select” option whenever it is linked to a UISelectOne component. You can use the includeNoSelectionOption attribute to override this behavior. The label used for the “no selection option” will default to “— Please Select —” but you can customize and internationalize this text easily by adding a org.springframework.context.MessageSource to your ApplicationContext that can resolve the code "spring.faces.noselectionoption".
On the subject of MessageSource, the <s:selectItems> component will, whenever possible, try to create the label of the SelectItem using a org.springframework.springfaces.message.ObjectMessageSource. I have blogged in the past about how to convert Objects to messages and this component simply makes use of those ideas.
The new component has helped us when creating the SelectItems to display, but what about dealing with form submit? How do you convert the submitted String option back to a real Object? in the initial example above, we are binding to JPA Customer entities; The values will display just fine, but when you submit a form a “Conversion Error” is displayed because JSF does not know how to get from the submitted String back to the Customer object. The usual answer here is to develop your own javax.faces.convert.Converter implementation, but this is often problematic. Often you your select item value will be some complex object that is hard to represent in its entirety as a String.
There is an interesting technique that you can use when writing a Converter that will be used with a UISelectOne or UISelectMany component. You actually only need write code to convert from the Object to a String, conversion in the other direction can be accomplished by iterating the SelectItems and returning the single Object value that, when converted to a String, matches your submitted value. You can read more about the idea in this blog post by Arjan Tijms. Using this technique with the <s:selectItems> component is really easy, simply provide a itemConverterStringValue attribute that will be used to create the unique getAsString() value:
<h:selectOneMenu> <s:selectItems value="#{customerRepository.all}" label="#{item.name}" itemConverterStringValue="#{item.id}"/> </h:selectOneMenu>
In actual fact the itemConverterStringValue is optional. If you don’t specify it, the toString() method of the object will be used or, in the case of a JPA @Entity, the @ID field will automatically be used. You are still free to write and attach your own Converter if you need to, in such cases the itemConverterStringValue is ignored.
Finally that is one more trick that the <s:selectItems> can perform. If you select component is bound to a Boolean or an Enum then the value attribute can be completely omitted. The select items will be built based on all possible options that the binding supports (“Yes”/”No” for Booleans or the complete set of Enum values). This also works with typed collections. For example, the following will display the options “Java”, “Spring” and “JavaServer Faces” (assuming you have an appropriate ObjectMessageSource) :
public enum Technology { JAVA, SPRING, JAVASERVER_FACES }
public class Bean implements Serializable { private Set<Technology> technologies = new HashSet<Technology>(); // ... getters and setters }
<h:selectManyCheckbox value="#{bean.technologies}"> <s:selectItems/> </h:selectManyCheckbox>
Reference: Integrating Spring & JavaServer Faces : Select Items from our JCG partner Phillip Webb at the Phil Webb’s Blog blog.