JSF 2.0:为selectOneMenu使用Enum值[复制]

时间:2023-01-26 20:02:24

This question already has an answer here:

这个问题已经有了答案:

I'm using JSF 2.0 and want to fill a selectOneMenu with the values of my Enum. A simple example:

我使用的是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;
  }
}

Unfortunately, i can't use Seam for my current project, which had a nice <s:convertEnum/> Tag that did most of the work. In Seam, to use the values of the Enum, i had to write the following markup (and create a factory that provides the #{genderValues}:

不幸的是,我不能为我当前的项目使用Seam,它有一个很好的 标记,它完成了大部分工作。在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>

The result is that i don't have to declare the Enum values explicitely anymore inside the markup. I know that this is not very easy in JSF <2.0, but is there any new in JSF2 to help with this issue? Or does Weld help here somehow? If there is nothing new in JSF2, what's the easiest way to do it in JSF 1.2?

结果是,我不需要在标记中明确地声明Enum值。我知道在JSF <2.0中这不是很容易,但是在JSF2中有什么新的东西可以帮助解决这个问题吗?还是焊接在这里有帮助?如果JSF2中没有新的内容,那么在JSF 1.2中最简单的方法是什么?

Or can i even integrate the Seam JSF tag and the corresponding classes of Seam to get the same feature in a JavaEE6-App (without the Seam container)?

或者,我甚至可以集成Seam JSF标签和相应的Seam类来在javaee6应用程序中获得相同的特性(没有Seam容器)?

5 个解决方案

#1


46  

Ok, here is the final way: - Register the standard enum converter in faces-config.xml (optional):

好的,这里是最后的方法:-在faces-config中注册标准的enum转换器。xml(可选):

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

Add a function for example to a managed bean which converts the Enum values to an array of SelectItems:

向托管bean中添加一个函数,该函数将枚举值转换为一个selectitem数组:

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

Then bind this function to the selectOneMenu in JSF:

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

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

That's it! Not the first explanation for this problem on the net. But i think it's the easiest & shortest one ;)

就是这样!这不是网上第一个解释。但我认为它是最简单和最短的;

#2


22  

After looking at my own Seam example for a minute I created a method in a managed bean like this :

在查看了我自己的Seam示例之后,我在一个托管bean中创建了一个方法:

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

And in my markup I put

在我的标记中。

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

Now I'll have to see if the enum is saved correctly in my entity when the form is sent. I'll see if I can do this myself - anyway, I would appreciate tips or best practices on this!

现在,我必须查看在发送表单时,是否在我的实体中正确保存了enum。我想看看我自己能不能做到这一点——无论如何,我会非常感谢你的建议或最佳做法!

#3


5  

Here's a simpler method which uses a simple getter and setter to marshal strings to enums.

这里有一个简单的方法,它使用一个简单的getter和setter来将字符串封为枚举。

http://www.ninthavenue.com.au/blog/using-enums-in-el

http://www.ninthavenue.com.au/blog/using-enums-in-el

#4


4  

I run into this issue some time ago and I solved it as you did, but then I realized at some point that I with that solution I could not use i18n because the strings were hardcoded in the enum class. So I modified my enumConverter to use messages for rendering.

我在一段时间之前遇到过这个问题,我像你一样解决了这个问题,但后来我意识到,我有了这个解决方案,我不能使用i18n,因为字符串在enum类中是硬编码的。因此,我修改了我的enumConverter以使用消息来呈现。

Also sometimes you may want to render the enum as some unique identifier and not as user readable text (for internal usage inside some components).

有时,您可能希望将enum作为一些惟一标识符,而不是作为用户可读文本(在某些组件内部使用)。

This is my converter:

这是我的转换器:

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

    }
}

#5


2  

I use this simple approach, it is quite optimistic, you can customize it for your own purpose. I put the following code in a reusable bean, that can be called from your application at any time, so you can use any of your enums declared in your package.

我使用这个简单的方法,它非常乐观,您可以定制它为您自己的目的。我将下面的代码放入可重用的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);
        }

Now use it in the xhtml file as follows:

现在在xhtml文件中使用它:

<p:selectOneMenu value="#{jobapp.aplicant.marital}">
<f:selectItems value="#{rtutil.fromEnum('com.company.package.enMarital')}" var="m" itemLabel="#{m}" itemValue="#{m}"/>
</p:selectOneMenu>

#1


46  

Ok, here is the final way: - Register the standard enum converter in faces-config.xml (optional):

好的,这里是最后的方法:-在faces-config中注册标准的enum转换器。xml(可选):

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

Add a function for example to a managed bean which converts the Enum values to an array of SelectItems:

向托管bean中添加一个函数,该函数将枚举值转换为一个selectitem数组:

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

Then bind this function to the selectOneMenu in JSF:

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

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

That's it! Not the first explanation for this problem on the net. But i think it's the easiest & shortest one ;)

就是这样!这不是网上第一个解释。但我认为它是最简单和最短的;

#2


22  

After looking at my own Seam example for a minute I created a method in a managed bean like this :

在查看了我自己的Seam示例之后,我在一个托管bean中创建了一个方法:

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

And in my markup I put

在我的标记中。

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

Now I'll have to see if the enum is saved correctly in my entity when the form is sent. I'll see if I can do this myself - anyway, I would appreciate tips or best practices on this!

现在,我必须查看在发送表单时,是否在我的实体中正确保存了enum。我想看看我自己能不能做到这一点——无论如何,我会非常感谢你的建议或最佳做法!

#3


5  

Here's a simpler method which uses a simple getter and setter to marshal strings to enums.

这里有一个简单的方法,它使用一个简单的getter和setter来将字符串封为枚举。

http://www.ninthavenue.com.au/blog/using-enums-in-el

http://www.ninthavenue.com.au/blog/using-enums-in-el

#4


4  

I run into this issue some time ago and I solved it as you did, but then I realized at some point that I with that solution I could not use i18n because the strings were hardcoded in the enum class. So I modified my enumConverter to use messages for rendering.

我在一段时间之前遇到过这个问题,我像你一样解决了这个问题,但后来我意识到,我有了这个解决方案,我不能使用i18n,因为字符串在enum类中是硬编码的。因此,我修改了我的enumConverter以使用消息来呈现。

Also sometimes you may want to render the enum as some unique identifier and not as user readable text (for internal usage inside some components).

有时,您可能希望将enum作为一些惟一标识符,而不是作为用户可读文本(在某些组件内部使用)。

This is my converter:

这是我的转换器:

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

    }
}

#5


2  

I use this simple approach, it is quite optimistic, you can customize it for your own purpose. I put the following code in a reusable bean, that can be called from your application at any time, so you can use any of your enums declared in your package.

我使用这个简单的方法,它非常乐观,您可以定制它为您自己的目的。我将下面的代码放入可重用的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);
        }

Now use it in the xhtml file as follows:

现在在xhtml文件中使用它:

<p:selectOneMenu value="#{jobapp.aplicant.marital}">
<f:selectItems value="#{rtutil.fromEnum('com.company.package.enMarital')}" var="m" itemLabel="#{m}" itemValue="#{m}"/>
</p:selectOneMenu>