Select lists with Seam

From Shrubbery

Jump to: navigation, search


Contents

[edit] Selecting from a list of entities

See http://docs.jboss.org/seam/latest/reference/en/html/controls.html

  • Use <s:selectItems> to produce a list of labeled select items from a list of entities.
  • Use <s:convertEntity> 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 <h:selectOneMenu> directly to the property of the referencing entity (e.g. a property that is a many-to-one).
<h:selectOneMenu value="#{person.continent}" required="true">                        (1)
    <s:selectItems value="#{continents.resultList}" var="continent"                  (2)
                   label="#{continent.name}" noSelectionLabel="Please Select..."/>
    <s:convertEntity />                                                              (3)
</h:selectOneMenu>
  1. 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.
  2. 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.
  3. s:convertEntity will convert the Continent entities into values for the HTML select, and vice versa.

[edit] Tips

  1. To avoid LazyInitializationExceptions and/or writing extra code in your EJB/Controller bean to initialize objects, use session in view.
  2. For required fields, put required="true" on the selectOneMenu and override javax.faces.component.UIInput.REQUIRED in messages.properties (see Standard Faces Error Messages).

[edit] Select from an enum

This works just like selecting an entity, but <s:convertEnum/> is used instead.


XHTML:

<h:selectOneMenu id="marketStatus" value="#{person.status}"         (1)
                 required="true">
    <s:selectItems value="#{enumLists.statusArray}" var="status"    (2)
                   label="#{status}"
                   noSelectionLabel="Select a status..."/>
    <s:convertEnum/>
</h:selectOneMenu>

EnumLists.java:

@Name("enumLists")
@Scope(ScopeType.STATELESS)
public class EnumLists
{
    public Status[] getStatusArray()
    {
        return Status.values();
    }
}
  1. person is an entity that has been outjected into the conversation. It has a 'status' property which is an enum.
  2. 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.

[edit] Multi-select from an enum

Here we use a selectManyCheckbox.


<h:selectManyCheckbox id="roles" 
                      layout="pageDirection" value="#{person.roles}"
                      required="true">
    <s:selectItems value="#{enumLists.roleArray}" var="role"
                   label="#{role}"/>
    <s:convertEnum/>
</h:selectManyCheckbox>

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.
 * <br>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();
    }

}
Personal tools