JSF 2.0: 使用枚举值作为selectOneMenu的选项

28

我在使用JSF 2.0,希望用我的Enum的值来填充selectOneMenu。

// Sample Enum
public enum Gender {
  MALE("Male"),
  FEMALE("Female");

  private final String label;

  private Gender(String label) {
    this.label = label;
  }

  public String getLabel() {
    return this.label;
  }
}

非常遗憾,我无法在当前项目中使用 Seam,因为它有一个不错的 <s:convertEnum/> 标签可以完成大部分工作。 在 Seam 中,为了使用 Enum 的值,我必须编写以下标记(并创建一个提供 #{genderValues} 的工厂):

<!-- the Seam way -->
<h:selectOneMenu id="persongender" value="#{person.gender}">
  <s:selectItems var="_gender" value="#{genderValues}"" label="#{_gender.label}"/>
  <s:convertEnum/>
</h:selectOneMenu>
结果是我不再需要在标记内显式地声明枚举值。 我知道在JSF<2.0中这并不容易,但JSF2中是否有任何新的方法来解决这个问题?或者Weld在这方面是否有所帮助? 如果JSF2中没有任何新功能,那么在JSF 1.2中最简单的方法是什么? 或者,我甚至可以集成Seam JSF标签和Seam的相应类,以在JavaEE6-App中获得相同的功能(无需Seam容器)?

4
请参阅[如何在selectOneMenu中使用枚举](https://dev59.com/72sy5IYBdhLWcg3w6yQN)和[如何在selectManyMenu中使用枚举](https://dev59.com/dW865IYBdhLWcg3wa980)。 - BalusC
5个回答

48

好的,这是最终的方法: - 在faces-config.xml中注册标准的枚举转换器(可选):

<converter>
  <converter-for-class>java.lang.Enum</converter-for-class>
  <converter-class>javax.faces.convert.EnumConverter</converter-class>
</converter>

在托管 bean 中添加一个函数,将枚举值转换为 SelectItems 数组的示例:
@ManagedBean
public class GenderBean {
  public SelectItem[] getGenderValues() {
    SelectItem[] items = new SelectItem[Gender.values().length];
    int i = 0;
    for(Gender g: Gender.values()) {
      items[i++] = new SelectItem(g, g.getLabel());
    }
    return items;
  }
}

然后将此函数绑定到JSF中的SelectOneMenu:

<h:selectOneMenu id="gender" value="#{person.gender}">
  <!-- use property name not method name -->
  <f:selectItems value="#{genderBean.genderValues}" />
</h:selectOneMenu>

就是这样了!这个问题在网络上已经有了很多解释。但我认为这个是最简单和最短的一个 ;)


43
不需要进行转换器注册。 - BalusC

21

看了一下我自己的Seam示例,我创建了一个托管Bean中的方法,如下所示:

@ManagedBean
public class MyManagedBean {
  public Gender[] getGenderValues() {
    return Gender.values;
  }
}   

在我的标记中,我放置了

<h:selectOneMenu id="gender" value="#{person.gender}">
  <f:selectItems value="#{myManagedBean.genderValues}" var="g" 
    itemValue="#{g}" itemLabel="#{g.label}"/>
</h:selectOneMenu>

现在我需要看看当表单被发送时,enum是否被正确地保存在我的实体中。我会尝试自己解决这个问题 - 无论如何,我会感激关于此的提示或最佳实践!


5

4
我之前遇到了这个问题,我像你一样解决了它,但后来我意识到,由于枚举类中的字符串是硬编码的,所以我无法使用i18n。因此,我修改了我的enumConverter,使用messages进行呈现。
此外,有时您可能希望将枚举呈现为某些唯一标识符,而不是作为用户可读文本(用于某些组件内部的内部使用)。
这是我的转换器:
import java.util.ResourceBundle;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */




import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;

import com.eyeprevent.configuration.ConfigurationReader;


/**
 * converts an enum in a way that makes the conversion reversible (sometimes)
 * <ul>
 * <li>input: uses its classname and ordinal, reversible<li>
 * <li>else: uses its name, non reversible<li>
 * </ul>
 */
public class EnumConverter implements Converter
{
    @SuppressWarnings("unchecked")
    public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException
    {
        if (value == null || value.length() < 1)
        {
            return null;
        }

        int pos = value.indexOf('@');
        if (pos < 0)
        {
            throw new IllegalArgumentException(value + " do not point to an enum");
        }

        String className = value.substring(0, pos);
        Class clazz;
        int ordinal = Integer.parseInt(value.substring(pos+1), 10);

        try
        {
            clazz = Class.forName( className, true, Thread.currentThread().getContextClassLoader() );
            // if the clazz is not an enum it might be an enum which is inherited. In this case try to find the superclass.
            while (clazz != null && !clazz.isEnum())
            {
                clazz = clazz.getSuperclass();
            }
            if (clazz == null)
            {
                throw new IllegalArgumentException("class " + className + " couldn't be treated as enum");
            }

            Enum[] enums = (Enum[]) clazz.getEnumConstants();
            if (enums.length >= ordinal)
            {
                return enums[ordinal];
            }
        }
        catch (ClassNotFoundException e1)
        {
            throw new RuntimeException(e1);
        }

        throw new IllegalArgumentException("ordinal " + ordinal + " not found in enum " + clazz);
    }

    public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException
    {
        if (value == null)
        {
            return "";
        }

        Enum<?> e = (Enum<?>) value;

        if (component instanceof UIInput || UIInput.COMPONENT_FAMILY.equals(component.getFamily()))
        {
            return e.getClass().getName() + "@" + Integer.toString(e.ordinal(), 10);
        }
        ResourceBundle messages =ConfigurationReader.getMessages(context.getViewRoot().getLocale());
        return messages.getString(e.name());

    }
}

2

我使用这个简单的方法,它非常乐观,你可以根据自己的目的进行定制。我将以下代码放在一个可重用的bean中,您可以随时从应用程序中调用它,因此您可以使用在您的包中声明的任何枚举。

public List<String> fromEnum(String cname) {
        List<String> names = new ArrayList<>();
        try {
            Class c = Class.forName(cname);
            Object[] r = c.getEnumConstants();
            if (r != null) {
                for (Object o : r) {
                    names.add(o.toString());
                }
            }
        } catch (ClassNotFoundException ex) {
            FaceUtil.ShowError(ex);
        }
        return names;
    }
public static void ShowError(Exception ex) {
        FacesMessage msg=new FacesMessage(FacesMessage.SEVERITY_ERROR,ex.getMessage(),"Error Message");
        FacesContext.getCurrentInstance().addMessage(null, msg);
        }

现在,您可以按照以下方式在xhtml文件中使用它:
<p:selectOneMenu value="#{jobapp.aplicant.marital}">
<f:selectItems value="#{rtutil.fromEnum('com.company.package.enMarital')}" var="m" itemLabel="#{m}" itemValue="#{m}"/>
</p:selectOneMenu>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接