Selecting from a list of entities
Use to produce a list of labeled select items from a list of entities.
Use to map back and forth between the select items and the actual entity values. This is what allows you to map the value of the directly to the property of the referencing entity (e.g. a property that is a many-to-one).
(1)
label="#{continent.name}" noSelectionLabel="Please Select..."/>
(3)
person is an entity that has been outjected into the
conversation. It has a 'continent' property which is many-to-one
association with another entity.
continents is a Seam application framework 'query'
object. This 'query' object should probably use a Seam-managed
EntityManager because we want have the Hibernate session-in-view behavior so we don't get lazy initialization exceptions when rendering the labels, etc.
s:convertEntity will convert the Continent entities into values for the HTML select, and vice versa.
Tips
To avoid LazyInitializationExceptions and/or writing extra code in your EJB/Controller bean to initialize objects, use session in view.
For required fields, put required="true" on the selectOneMenu and override javax.faces.component.UIInput.REQUIRED in messages.properties (see Standard Faces Error Messages).
Select from an enum
This works just like selecting an entity, but is used instead.
XHTML:
required="true">
label="#{status}"
noSelectionLabel="Select a status..."/>
EnumLists.java:
@Name("enumLists")
@Scope(ScopeType.STATELESS)
public class EnumLists
{
public Status[] getStatusArray()
{
return Status.values();
}
}
person is an entity that has been outjected into the conversation. It has a 'status' property which is an enum.
We need to expose the values of the enum as a list or an
array, so we make a stateless POJO component with getters that returns
arrays for various enums called enumLists.
Multi-select from an enum
layout="pageDirection" value="#{person.roles}"
required="true">
label="#{role}"/>
Unfortunately, Seam's convertEnum can't handle multi selects yet. This example will yeild a strange exception:
java.lang.IllegalArgumentException: java.util.List is not an enum type
Luckily, it's very easy to create custom converter tags with Facelets. Here is the converter class that handles both ordinary enums and multi-selects:
package eg;
import javax.faces.component.*;
import javax.faces.context.*;
import javax.faces.convert.*;
import javax.faces.el.ValueBinding;
import java.util.List;
import java.util.Collection;
/**
* Converter for enum multi-selects.
*
User: Joshua Davis
* Date: May 16, 2007
* Time: 7:25:58 AM
*/
public class EnumListConverter implements Converter
{
@SuppressWarnings({"unchecked"})
public Object getAsObject(FacesContext context,
UIComponent comp,
String value)
throws ConverterException
{
ValueBinding binding = comp.getValueBinding("value");
Class enumType = binding.getType(context);
if (enumType.isEnum()) // Single enum?
return Enum.valueOf(enumType, value);
else // List of enums.
{
// Find the s:selectItems so we can get the enum.
List children = comp.getChildren();
for (Object child : children)
{
if (child instanceof UIComponent)
{
UIComponent c = (UIComponent) child;
ValueBinding b = c.getValueBinding("value");
Class t = b.getType(context);
// Array of enums: use the component type.
if (t.isArray() && t.getComponentType().isEnum())
{
t = t.getComponentType();
return Enum.valueOf(t,value);
}
else
{
Object v = b.getValue(context);
// Collection of enum values, get the type of the first element.
if (v instanceof Collection)
{
t = ((Collection) v).iterator().next().getClass();
return Enum.valueOf(t,value);
}
}
}
}
throw new ConverterException("Unable to find selectItems with enum values!");
}
}
public String getAsString(FacesContext context,
UIComponent component,
Object object)
throws ConverterException
{
if (object == null) {
return null;
}
return ((Enum) object).name();
}
}