Using a List of Custom Objects in JSF/Seam

Posted by & filed under Uncategorized.

Whether you are using radio buttons, check boxes or selects,  at some point you probably have a collection of custom objects that you want to supply to that form control.  JSF by itself knows about SelectItem objects to populate the list and only really knows about Strings when a value is selected and submitted to the server.  Seam provides some automatic conversions; <s:selectItems will take a generic collection and turn it into a collection of SelectItem objects.  On post-back, JSF provides some built in converters; for dates <f:convertDateTime and numbers <f:convertNumber.  Seam also provides some automatic conversions on the post-back; enums <s:convertEnum and entity beans <s:convertEntity.  When you use a list of custom objects, the post-back will require an extra conversion step to convert your string back to the object you desire, otherwise you’ll get a ClassCastException.  There are couple different ways to do this.  One way would be to use Strings as the data type in your backing bean and convert it to the object you want in your setter method.  Another way is to implement a Converter.  For example:

package model.ui;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;

import model.CustomObject;

public class CustomObjectConverter implements Converter {

public CustomObjectConverter() {
      super();
}

/**
  * @see javax.faces.convert.Converter#getAsObject(javax.faces.context.FacesContext,   javax.faces.component.UIComponent, java.lang.String)
*/
public Object getAsObject(FacesContext arg0, UIComponent arg1, String key) {
      return Lookup.lookupKey(key);
}

/**
  * @see javax.faces.convert.Converter#getAsString(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.Object)
*/
public String getAsString(FacesContext arg0, UIComponent arg1, Object o) {
    CustomObject co = (CustomObject)o;
    return co.getKey();
}

}

Register it in the faces-config.xml for all CustomObject objects:
<converter>
<converter-for-class>
model.CustomObject
</converter-for-class>
<converter-class>
model.ui.CustomObjectConverter
</converter-class>
</converter>

Say I have an object called CustomObject that has a key and a name attribute, and a Seam component that provides a List of CustomObject objects.  My Facelets template looks like so:
<h:selectOneMenu
value=”#{myBackingBean.customObject}”>
<s:selectItems
value=”#{Lookup.customObjects}”
var=”option”
label=”#{option.name}” />
</h:selectOneMenu>

My Seam component, Lookup, provides the list of CustomObject objects thru Lookup.customObjects to the <s:selectItems tag which converts them to a list of JSF SelectItem objects.  On post-back, the CustomObject attribute key has its value sent as a String back to the server.  JSF then converts the string using the converter defined above and sets the getAsObject return value on myBackingBean.customObject setter.

Seems like quite a bit of work just to pass a value to the server.