【问题标题】:Use enum in h:selectManyCheckbox在 h:selectManyCheckbox 中使用枚举
【发布时间】:2011-04-18 20:01:44
【问题描述】:

我想在<h:selectManyCheckbox> 中使用枚举值。复选框已正确填充,但是,在选择某些值并提交它们时,它们的运行时类型是String,而不是枚举。我的代码:

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

UserController 类(SecurityRole 是枚举类型):

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 调用 setRoles 方法时,它包含一个字符串类型的列表,而不是枚举类型。有任何想法吗?谢谢!

【问题讨论】:

    标签: jsf jsf-2 enums selectmanycheckbox


    【解决方案1】:

    此问题与枚举无关。对于 JSF 具有内置转换器的其他 List 类型,您会遇到同样的问题,例如List&lt;Integer&gt;List&lt;Double&gt;,等等。

    问题在于 EL 在运行时运行,而泛型类型信息在运行时丢失。所以本质上,JSF/EL 对List 的参数化类型一无所知,并且默认为String,除非由显式Converter 另行指定。理论上,在ParameterizedType#getActualTypeArguments() 的帮助下使用令人讨厌的反射黑客是可能的,但 JSF/EL 开发人员可能有他们不这样做的原因。

    您确实需要为此明确定义一个转换器。由于 JSF 已经内置了 EnumConverter(在这种特殊情况下不能独立使用,因为您必须在运行时指定枚举类型),您可以按如下方式扩展它:

    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);
        }
    
    }
    

    并按如下方式使用:

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

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

    更通用(和 hacky)的解决方案是将枚举类型存储为组件属性。

    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));
            }
        }
    
    }
    

    它可用于使用转换器 ID genericEnumConverter 的各种 List&lt;Enum&gt;。对于List&lt;Double&gt;List&lt;Integer&gt; 等,可以使用内置转换器javax.faces.Doublejavax.faces.Integer 等。由于无法从视图侧指定目标枚举类型(Class&lt;Enum&gt;),内置枚举转换器顺便说一句不合适。 JSF 实用程序库OmniFaces 正好提供了这个转换器out the box

    请注意,对于普通的Enum 属性,内置的EnumConverter 已经足够了。 JSF 将使用正确的目标枚举类型自动实例化它。

    【讨论】:

    • 我不会说这是一个讨厌的反射黑客。框架做到了,更多的类型信息,更多的快乐。 JSF 的人可能对他们创作的复杂性感到不知所措,他们没有时间做这些。
    • @BalusC,感谢您提供另一个信息丰富且有用的答案。如果我可以这么大胆,请问您为什么选择扩展 EnumConverter 而不是委托给它的一个实例?
    • @Nick:为了通过super设置所需的枚举类。
    • 在 OmniFaces 3.0 中,GenericEnumConverter 消失了,因为这已在 JSF 2.3 中解决。详情请见github.com/omnifaces/omnifaces/issues/286
    【解决方案2】:

    在某些情况下,List 也可以是数组 SomeType[],在这种情况下不需要显式转换器。

    泛型擦除是一种在不破坏旧东西的情况下将泛型放入语言中的聪明方法,但现在我们永远生活在这个决定的后果中......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-29
      • 2023-04-04
      • 2012-03-20
      • 2012-03-04
      • 2011-06-23
      • 2021-11-20
      相关资源
      最近更新 更多