tag selectonelistbox radiobutton examples jsf jsf-2 enums selectmanycheckbox

selectonelistbox - jsf tag reference



Use enum en h: selectManyCheckbox (2)

Quiero usar los valores de enumeración en un <h:selectManyCheckbox> . Las casillas de verificación se rellenan correctamente, sin embargo, al seleccionar algunos valores y enviarlos, su tipo de tiempo de ejecución es String y no enumeración. Mi código:

<h:selectManyCheckbox value="#{userController.roles}" layout="pageDirection"> <f:selectItems value="#{userController.rolesSelectMany}" /> </h:selectManyCheckbox>

Clase UserController (SecurityRole es un tipo de enumeración):

public SelectItem[] getRolesSelectMany() { SelectItem[] items = new SelectItem[SecurityRole.values().length]; int i = 0; for (SecurityRole role : SecurityRole.values()) { items[i++] = new SelectItem(role, role.toString()); } return items; } public List<SecurityRole> getRoles() { getCurrent().getRoles(); } public void setRoles(List<SecurityRole> roles) { getCurrent().setRoles(roles); }

Cuando JSF llama al método setRoles, contiene una lista de tipo String y no el tipo enum. ¿Algunas ideas? ¡Gracias!


En algunos casos, la Lista podría ser una matriz SomeType [] , y en este caso no se necesita un convertidor explícito.

El borrado genérico era una forma inteligente de poner genéricos en el lenguaje sin romper las cosas viejas, pero ahora vivimos para siempre con las consecuencias de esa decisión ...


Este problema no está específicamente relacionado con enumeraciones. Tendría el mismo problema con otros tipos de List para los cuales JSF tiene convertidores incorporados, por ejemplo, List<Integer> , List<Double> , etc.

El problema es que EL opera el tiempo de ejecución y que la información de tipo genérico se pierde durante el tiempo de ejecución. Así que, en esencia, JSF / EL no sabe nada sobre el tipo parametrizado de la List y por defecto es String menos que se especifique lo contrario por un Converter explícito. En teoría, hubiera sido posible usar hacks de reflexión desagradables con la ayuda de ParameterizedType#getActualTypeArguments() , pero los desarrolladores de JSF / EL pueden tener sus razones para no hacerlo.

Realmente necesitas definir explícitamente un convertidor para esto. Dado que JSF ya se envía con un EnumConverter (que no se puede EnumConverter forma independiente en este caso particular porque tiene que especificar el tipo de enumeración durante el tiempo de ejecución), podría ampliarlo de la siguiente manera:

package com.example; import javax.faces.convert.EnumConverter; import javax.faces.convert.FacesConverter; @FacesConverter(value="securityRoleConverter") public class SecurityRoleConverter extends EnumConverter { public SecurityRoleConverter() { super(SecurityRole.class); } }

Y utilízalo como sigue:

<h:selectManyCheckbox value="#{userController.roles}" converter="securityRoleConverter"> <f:selectItems value="#{userController.rolesSelectMany}" /> </h:selectManyCheckbox>

o

<h:selectManyCheckbox value="#{userController.roles}"> <f:converter converterId="securityRoleConverter" /> <f:selectItems value="#{userController.rolesSelectMany}" /> </h:selectManyCheckbox>

Una solución un poco más genérica (y hacky) sería almacenar el tipo de enumeración como atributo de componente.

package com.example; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; import javax.faces.convert.FacesConverter; @FacesConverter(value="genericEnumConverter") public class GenericEnumConverter implements Converter { private static final String ATTRIBUTE_ENUM_TYPE = "GenericEnumConverter.enumType"; @Override public String getAsString(FacesContext context, UIComponent component, Object value) { if (value instanceof Enum) { component.getAttributes().put(ATTRIBUTE_ENUM_TYPE, value.getClass()); return ((Enum<?>) value).name(); } else { throw new ConverterException(new FacesMessage("Value is not an enum: " + value.getClass())); } } @Override @SuppressWarnings({"rawtypes", "unchecked"}) public Object getAsObject(FacesContext context, UIComponent component, String value) { Class<Enum> enumType = (Class<Enum>) component.getAttributes().get(ATTRIBUTE_ENUM_TYPE); try { return Enum.valueOf(enumType, value); } catch (IllegalArgumentException e) { throw new ConverterException(new FacesMessage("Value is not an enum of type: " + enumType)); } } }

Se puede utilizar en todo tipo de List<Enum> utilizando el ID de convertidor genericEnumConverter . Para List<Double> , List<Integer> , etc. uno habría usado los convertidores javax.faces.Double , javax.faces.Integer y así sucesivamente. El convertidor Enum incorporado es, por cierto, inadecuado debido a la incapacidad de especificar el tipo de enumeración objetivo (una Class<Enum> ) desde el lado de la vista. La biblioteca de utilidades JSF, OmniFaces, ofrece exactamente este convertidor fuera de la caja .

Tenga en cuenta que para una propiedad Enum normal, el EnumConverter ya es suficiente. JSF lo creará automáticamente con el tipo de enumeración objetivo correcto.