Cómo usar valores de enumeración en f: selectItem (s)


103

Quiero hacer un menú desplegable selectOneMenu para poder seleccionar un estado en mi pregunta. ¿Es posible hacer que f: selectItem sea más flexible considerando lo que sucede si cambia el orden de las enumeraciones y si la lista es grande? ¿Y podría hacer esto mejor? ¿Y es posible "seleccionar" automáticamente el elemento que tiene la pregunta?

Clase de enumeración

public enum Status {
    SUBMITTED,
    REJECTED,
    APPROVED
}

Entidad de pregunta

@Enumerated(EnumType.STRING)
private Status status;

JSF

<div class="field">
    <h:outputLabel for="questionStatus" value="Status" />
    <h:selectOneMenu id="questionStatus" value="#{bean.question.status}" >
        <f:selectItem itemLabel="Submitted" itemValue="0" />
        <f:selectItem itemLabel="Rejected" itemValue="1" />
        <f:selectItem itemLabel="Approved" itemValue="2" />
    </h:selectOneMenu>
    <hr />
</div>

Respuestas:


210

JSF tiene un convertidor incorporado para enum, así que esto debería funcionar:

@ManagedBean
@ApplicationScoped
public class Data {

    public Status[] getStatuses() {
        return Status.values();
    }

}

con

<h:selectOneMenu value="#{bean.question.status}" >
    <f:selectItems value="#{data.statuses}" />
</h:selectOneMenu>

(nota: dado que JSF 2.0 ya no es necesario proporcionar un SelectItem[]o List<SelectItem>, un T[]y también List<T>se aceptan y puede acceder al elemento actual por varatributo)

Si usa la biblioteca de utilidades JSF OmniFaces , entonces podría usar en <o:importConstants>lugar de un bean.

<o:importConstants type="com.example.Status" />

<h:selectOneMenu value="#{bean.question.status}" >
    <f:selectItems value="#{Status}" />
</h:selectOneMenu>

Si también tiene la intención de controlar las etiquetas, puede agregarlas a la Statusenumeración:

public enum Status {

    SUBMITTED("Submitted"),
    REJECTED("Rejected"),
    APPROVED("Approved");

    private String label;

    private Status(String label) {
        this.label = label;
    }

    public String getLabel() {
        return label;
    }

}

con

<f:selectItems value="#{data.statuses}" var="status"
    itemValue="#{status}" itemLabel="#{status.label}" />

O, mejor, convierta el valor de enumeración en una clave de propiedad de un paquete de recursos localizado (se requiere EL 3.0):

<f:selectItems value="#{data.statuses}" var="status"
    itemValue="#{status}" itemLabel="#{text['data.status.' += status]}" />

con esto en un archivo de propiedades asociado con el paquete de recursos #{text}

data.status.SUBMITTED = Submitted
data.status.REJECTED = Rejected
data.status.APPROVED = Approved

Una cosa, BalusC, es que es posible "seleccionar" / ver el estado que tiene una pregunta como predeterminado (por ejemplo, cuando está editando una pregunta, entonces ya ha establecido el estado de la pregunta en algo)
— LuckyLuke

En el ejemplo anterior, JSF lo hará de forma predeterminada cuando #{bean.question.status}tenga un valor de enumeración válido. No necesita hacer nada para asegurarse de que questiontiene la propiedad de estado adecuada precargada.
— BalusC

@BalusC ¿Cómo acceder al valor ordinal desde JSF?
— jacktrades

2
Si, como yo, obtiene una excepción de formato de número para += status, intente usarlo .concat(status)como sugiere @Ziletka.
— whistling_marmot

Si prefiere java.util.List, simplemente puede modificar el tipo de retorno getStatuses () a List <Status> y devolver Arrays.asList (Status.values ​​());
— stakahop

16

Para la localización también podemos utilizar esta solución:

public enum Status { SUBMITTED, REJECTED, APPROVED }

data.status.SUBMITTED=Submitted
data.status.REJECTED=Rejected
data.status.APPROVED=Approved

<h:selectOneMenu value="#{bean.question.status}" >
    <f:selectItems
        value="#{data.statuses}"
        var="status"
        itemValue="#{status}"
        itemLabel="#{text['data.status.'.concat(status)]}" />
</h:selectOneMenu>

Entonces, la ruta de recursos para las cadenas de localización no está codificada en Enum.


1
Tenga en cuenta que esta sintaxis solo se admite desde EL 2.2, que es "relativamente" nueva. De lo contrario, siempre puede tomar <c:set>o <ui:param>crear una función EL personalizada.
— BalusC

Gracias BalusC. ¿Es posible reemplazar de alguna manera # {data.statuses} con enum Class, sin usar un bean de respaldo (por ejemplo, value = "# {org.myproject.Status.values}")?
— sasynkamil

@BalusC, ¿estás seguro? Estoy usando GF 3.1.2 (Mojarra JSF 2.1.6).
— sasynkamil

4

Puede usar <f:selectItems value="#{carBean.carList}" />y devolver una lista de SelectIteminstancias que envuelvan la enumeración (use Status.values()para obtener todos los valores posibles).


2

Puede usar la siguiente función de utilidad el para obtener los valores de enumeración y usarlos en, SelectOneMenupor ejemplo. No es necesario crear frijoles y métodos repetitivos.

public final class ElEnumUtils
{
    private ElEnumUtils() { }

    /**
     * Cached Enumerations, key equals full class name of an enum
     */
    private final static Map<String, Enum<?>[]> ENTITY_ENUMS = new HashMap<>();;

    /**
     * Retrieves all Enumerations of the given Enumeration defined by the
     * given class name.
     *
     * @param enumClassName Class name of the given Enum.
     *
     * @return
     *
     * @throws ClassNotFoundException
     */
    @SuppressWarnings("unchecked")
    public static Enum<?>[] getEnumValues(final String enumClassName) throws ClassNotFoundException
    {
        // check if already cached - use classname as key for performance reason
        if (ElEnumUtils.ENTITY_ENUMS.containsKey(enumClassName))
            return ElEnumUtils.ENTITY_ENUMS.get(enumClassName);

        final Class<Enum<?>> enumClass = (Class<Enum<?>>) Class.forName(enumClassName);

        final Enum<?>[] enumConstants = enumClass.getEnumConstants();

        // add to cache
        ElEnumUtils.ENTITY_ENUMS.put(enumClassName, enumConstants);

        return enumConstants;
    }
}

Regístrelo como una función el en un archivo taglib:

<function>
    <description>Retrieves all Enumerations of the given Enumeration defined by the given class name.</description>
    <function-name>getEnumValues</function-name>
    <function-class>
        package.ElEnumUtils
    </function-class>
    <function-signature>
        java.lang.Enum[] getEnumValues(java.lang.String)
    </function-signature>
</function>

Y finalmente llámalo como:

<p:selectOneMenu value="#{bean.type}">
    <f:selectItems value="#{el:getEnumValues('package.BeanType')}" var="varEnum" 
        itemLabel="#{el:getEnumLabel(varEnum)}" itemValue="#{varEnum}"/>
</p:selectOneMenu>

Similar a la respuesta de BalusC, debería usar un paquete de recursos con etiquetas de enumeración localizadas y para un código más limpio también puede crear una función como getEnumLabel(enum)


No se necesita una "función" (método más), cuando puede usar #{myBundle[enumName.i18nKey]}y luego poner las claves i18n en su enumeración como propiedades: BLA_TYPE("SOME_BLA_TYPE_KEY")by BLA_TYPEes la enumeración que se utilizará y SOME_BLA_TYPE_KEYes la clave i18n.
— Roland
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.