Domanda

voglio usare valori enum in un <h:selectManyCheckbox>. Le caselle di controllo vengono popolati correttamente, tuttavia, quando si selezionano alcuni valori e di sottoporlo, il loro tipo di runtime è String, e non enum. Il mio codice:

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

class UserController (SecurityRole è un tipo 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);
}

Quando JSF chiama il metodo setRoles, contiene un elenco di tipo String, e non il tipo enum. Qualche idea? Grazie!

È stato utile?

Soluzione

Il problema è non specificamente legato alla enumerazioni. Si avrebbe lo stesso problema con altri tipi List per i quali JSF ha built-convertitori, ad esempio, List<Integer>, List<Double>, eccetera.

Il problema è che EL opera di runtime e che le informazioni di tipo generico viene persa durante il runtime. Quindi, in sostanza, JSF / EL non sa nulla circa il tipo parametrico del List e il valore predefinito è String se non diversamente specificato da un Converter esplicito. In teoria, sarebbe stato possibile utilizzando hack riflessione brutto con l'aiuto di ParameterizedType#getActualTypeArguments() , ma il JSF / sviluppatori di EL possono avere le loro ragioni per non farlo.

Hai davvero bisogno di definire in modo esplicito un convertitore per questo. Dal momento che JSF già fornito con un builtin EnumConverter ( che non è standalone utilizzabile in questo caso particolare perché si deve specificare il tipo enum durante il runtime), si può solo estendere come segue:

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 usarlo come segue:

<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>

Un po 'più generico (e hacky) soluzione sarebbe quella di memorizzare il tipo enum come attributo di 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));
        }
    }

}

E 'utilizzabile su tutti i tipi di List<Enum> utilizzando convertitore ID genericEnumConverter. Per List<Double>, List<Integer>, ecc si sarebbe usato il convertitori incorporati javax.faces.Double, javax.faces.Integer e così via. Il convertitore integrato Enum è dal modo idoneo a causa della incapacità di specificare il tipo di destinazione enum (a Class<Enum>) dal lato vista su. La libreria di utilità JSF OmniFaces offre esattamente questo convertitore la scatola .

Si noti che per una proprietà Enum normale, il EnumConverter incorporato sufficiente già. JSF sarà un'istanza automagicamente con il tipo di destinazione enum destra.

Altri suggerimenti

In alcuni casi il List potrebbe benissimo essere un array SomeType [] , e in questo caso è necessario un convertitore senza esplicito.

cancellazione Generico è un modo intelligente di mettere generici nella lingua senza rompere la roba vecchia, ma ora viviamo per sempre con le conseguenze di tale decisione ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top