Ce problème n'est pas spécifiquement lié à enums. Vous auriez le même problème avec d'autres types List
pour lesquels JSF a des convertisseurs intégrés, par ex. List<Integer>
, List<Double>
, etc.
Le problème est que EL exploite l'exécution et que les informations de type générique sont perdues pendant l'exécution. Donc, en substance, JSF/EL ne sait rien sur le type paramétré du List
et vaut String
par défaut sauf indication contraire par un Converter
explicite. En théorie, il aurait été possible d'utiliser des hacks de réflexion désagréables avec l'aide de ParameterizedType#getActualTypeArguments()
, mais les développeurs JSF/EL peuvent avoir leurs raisons pour ne pas le faire.
Vous avez vraiment besoin de définir explicitement un convertisseur pour cela. Depuis JSF déjà livré avec une commande interne 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 générique (et hacky) solution serait de stocker le type enum comme attribut de 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>
en utilisant ID convertisseur genericEnumConverter
. Pour List<Double>
, List<Integer>
, etc. on aurait utilisé les convertisseurs intégrés javax.faces.Double
, javax.faces.Integer
et ainsi de suite. Le convertisseur Enum intégré est d'ailleurs inadapté en raison de l'impossibilité de spécifier le type enum cible (un Class<Enum>
) du côté de la vue. La bibliothèque utilitaire JSF OmniFaces offre exactement ce convertisseur out the box. Notez que pour une propriété Enum
normale, le EnumConverter
intégré suffit déjà. JSF va l'instancier automagiquement avec le bon type enum cible.
Je ne dirais pas que c'est un mauvais reflet. cadres font cela, plus d'informations de type, plus de bonheur. Les gars de JSF sont probablement dépassés par la complexité de leur création, ils n'ont pas le temps pour ça. – irreputable
+1 pour le nouveau mot que j'ai appris: automagically :) – lamostreta
@BalusC, je vous remercie pour une autre réponse informative et utile. Si je peux être si gras, puis-je demander pourquoi vous avez choisi d'étendre 'EnumConverter' au lieu de déléguer à une instance de celui-ci? – Nick