Use enum em h:selectManyCheckbox
-
26-09-2019 - |
Pergunta
Eu quero usar os valores enum em um <h:selectManyCheckbox>
.As caixas de verificação preenchida corretamente, no entanto, ao selecionar alguns valores e submetê-los, o seu tipo de tempo de execução é String
, e não enum.O meu código:
<h:selectManyCheckbox value="#{userController.roles}" layout="pageDirection">
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
UserController classe (SecurityRole é um tipo de enumeração):
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);
}
Quando JSF chama a setRoles método, ele contém uma lista de tipo de Seqüência de caracteres, e não o tipo enum.Qualquer idéias?Obrigado!
Solução
Este problema não está relacionado especificamente para enums.Você teria o mesmo problema com outros List
tipos para os quais JSF tem incorporado a conversores, e.g. List<Integer>
, List<Double>
, etc.
O problema é que o opera tempo de execução e que tipo genérico de informação é perdida durante o tempo de execução.Então, em essência, JSF/EL não sabe nada sobre o parametrizada tipo de List
e padrões para String
a menos que especificado de outra forma por uma explícita Converter
.Em teoria, teria sido possível utilizar desagradável reflexão hacks com a ajuda de ParameterizedType#getActualTypeArguments()
, mas o JSF/EL desenvolvedores podem ter suas razões para não fazer isso.
Você realmente precisa definir explicitamente um conversor para isso.Desde o JSF já vem com um builtin EnumConverter
(o que não é utilizável autônomo, neste caso particular, porque você tem que especificar o tipo enum durante o tempo de execução), você pode apenas estender-lo da seguinte forma:
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);
}
}
E usa-o como segue:
<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>
Um pouco mais genérico (e hacky) solução seria armazenar o tipo enum 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));
}
}
}
É usável em todos os tipos de List<Enum>
usando o conversor de IDENTIFICAÇÃO genericEnumConverter
.Para List<Double>
, List<Integer>
, etc teria usado o builtin conversores javax.faces.Double
, javax.faces.Integer
e assim por diante.O grupo builtin Enum converter é a forma inadequada devido à incapacidade para especificar o alvo do tipo enum (um Class<Enum>
) a partir da vista de lado.O JSF biblioteca de utilitários OmniFaces oferece exatamente este conversor fora da caixa.
Observe que, para um normal Enum
propriedade, o grupo builtin EnumConverter
já basta.JSF irá instanciar ele automagicamente com o alvo certo tipo enum.
Outras dicas
Em alguns casos, o Lista também poderia ser uma matriz Algum dia [, e, neste caso, nenhum conversor explícito é necessário.
O apagamento genérico era uma maneira inteligente de colocar genéricos no idioma sem quebrar as coisas antigas, mas agora vivemos para sempre com as consequências dessa decisão ...