Utilisez ENUM dans h: selectManyCheckbox
-
26-09-2019 - |
Question
Je veux utiliser des valeurs ENUM dans un <h:selectManyCheckbox>
. Les cases à cocher se remplis correctement, cependant, lors de la sélection des valeurs et de les soumettre, leur type d'exécution est String
, et non ENUM. Mon code:
<h:selectManyCheckbox value="#{userController.roles}" layout="pageDirection">
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
classe UserController (SecurityRole est un type enum):
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);
}
JSF appelle la méthode setRoles, il contient une liste de type String, et non le type enum. Des idées? Merci!
La solution
Ce problème n'est pas lié spécifiquement à énumérations. Vous auriez le même problème avec d'autres types de List
pour lesquels JSF a builtin convertisseurs, par exemple List<Integer>
, List<Double>
, etcetera.
Le problème est que EL fonctionne et que l'exécution des informations de type générique est perdue lors de l'exécution. Donc, en substance, JSF / EL ne sait rien sur le type paramétrées du List
et par défaut String
, sauf indication contraire par un Converter
explicite. En théorie, il aurait été possible en utilisant hacks de réflexion désagréables avec l'aide de ParameterizedType#getActualTypeArguments()
, mais le JSF / développeurs EL peuvent avoir leurs raisons pour ne pas faire cela.
Vous avez vraiment besoin de définir explicitement un convertisseur pour cela. Depuis JSF déjà livré avec un EnumConverter
( qui n'est pas autonome utilisable dans ce cas particulier parce que vous devez spécifier le type enum lors de l'exécution), vous pouvez simplement l'étendre comme suit:
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);
}
}
et l'utiliser comme suit:
<h:selectManyCheckbox value="#{userController.roles}" converter="securityRoleConverter">
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
ou
<h:selectManyCheckbox value="#{userController.roles}">
<f:converter converterId="securityRoleConverter" />
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
Un peu plus de solution générique (et hacky) consisterait à stocker le type enum comme attribut du composant.
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));
}
}
}
Il est utilisable sur toutes sortes de List<Enum>
utilisant genericEnumConverter
ID convertisseur. Pour List<Double>
, List<Integer>
, etc on aurait utilisé les convertisseurs de BUILTIN javax.faces.Double
, javax.faces.Integer
et ainsi de suite. Le convertisseur intégré Enum est d'ailleurs inadaptés en raison de l'incapacité à spécifier le type cible enum (un Class<Enum>
) à partir du côté de la vue sur. La bibliothèque utilitaire JSF OmniFaces offre exactement ce convertisseur la case .
Notez que pour une propriété Enum
normale, la fonction interne EnumConverter
suffit déjà. JSF instancier automagiquement avec le bon type de cible ENUM.
Autres conseils
Dans certains cas, la Liste pourrait tout aussi bien être un tableau UnType [] , et dans ce cas convertisseur ne explicite est nécessaire.
effacement générique est une façon intelligente de mettre les médicaments génériques dans la langue sans casser les vieux trucs, mais maintenant nous vivons toujours avec les conséquences de cette décision ...