在 h:selectManyCheckbox 中使用枚举 [英] Use enum in h:selectManyCheckbox

查看:18
本文介绍了在 h:selectManyCheckbox 中使用枚举的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在 中使用枚举值.此复选框正确地填充,但是,在选择某些值并提交它们时,它们的运行时类型是字符串,而不是枚举.我的代码:

I want to use enum values in a <h:selectManyCheckbox>. The checkboxes get populated correctly, however, when selecting some values and submitting them, their runtime type is String, and not enum. My code:

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

UserController 类(SecurityRole 是一个枚举类型):

UserController class (SecurityRole is an enum type):

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 方法时,它包含一个 String 类型的列表,而不是 enum 类型.有任何想法吗?谢谢!

When JSF calls the setRoles method, it contains a list of type String, and not the enum type. Any ideas? Thanks!

推荐答案

这个问题与枚举没有特别的关系.对于 JSF 具有内置转换器的其他 List 类型,您会遇到同样的问题,例如ListList 等.

This problem is not specifically related to enums. You would have the same problem with other List types for which JSF has builtin converters, e.g. List<Integer>, List<Double>, etcetera.

问题是EL操作运行时,泛型类型信息在运行时丢失.所以本质上,JSF/EL 对 List 的参数化类型一无所知,并且默认为 String,除非显式的 Converter 另有指定>.理论上,在 ParameterizedType#getActualTypeArguments(),但 JSF/EL 开发人员可能有他们不这样做的原因.

The problem is that EL operates runtime and that generic type information is lost during runtime. So in essence, JSF/EL doesn't know anything about the parameterized type of the List and defaults to String unless otherwise specified by an explicit Converter. In theory, it would have been possible using nasty reflection hacks with help of ParameterizedType#getActualTypeArguments(), but the JSF/EL developers may have their reasons for not doing this.

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

You really need to explicitly define a converter for this. Since JSF already ships with a builtin EnumConverter (which isn't useable standalone in this particular case because you have to specify the enum type during runtime), you could just extend it as follows:

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)的解决方案是将枚举类型存储为组件属性.


A bit more generic (and hacky) solution would be to storing the enum type as component attribute.

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.对于 ListList 等,可以使用内置转换器 javax.faces.Doublejavax.faces.Integer 等等.顺便说一下,由于无法从视图侧指定目标枚举类型(Class),内置的 Enum 转换器不合适.JSF 实用程序库 OmniFaces 正好提供了这个转换器 开箱即用.

It's useable on all kinds of List<Enum> using converter ID genericEnumConverter. For List<Double>, List<Integer>, etc one would have used the builtin converters javax.faces.Double, javax.faces.Integer and so on. The builtin Enum converter is by the way unsuitable due to the inability to specify the target enum type (a Class<Enum>) from the view side on. The JSF utility library OmniFaces offers exactly this converter out the box.

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

Note that for a normal Enum property, the builtin EnumConverter already suffices. JSF will instantiate it automagically with the right target enum type.

这篇关于在 h:selectManyCheckbox 中使用枚举的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆