我正在使用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>
结果是我不必在标记内明确声明Enum值。 我知道在JSF&lt; 2.0中这不是很容易,但JSF2中是否有任何新的东西来帮助解决这个问题?或者Weld在某种程度上帮助了吗? 如果JSF2中没有新内容,那么在JSF 1.2中最简单的方法是什么?
或者我甚至可以集成Seam JSF标记和相应的Seam类来在JavaEE6-App中获得相同的功能(没有Seam容器)?
答案 0 :(得分:47)
好的,这是最后的方法: - 在faces-config.xml(可选)中注册标准枚举转换器:
<converter>
<converter-for-class>java.lang.Enum</converter-for-class>
<converter-class>javax.faces.convert.EnumConverter</converter-class>
</converter>
将一个函数添加到托管bean,该托管bean将Enum值转换为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>
就是这样!不是网上这个问题的第一个解释。但我认为这是最简单的&amp;最短的一个;)
答案 1 :(得分: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
是否在我的实体中正确保存。我会看看自己能不能做到这一点 - 无论如何,我会很感激提示或最佳实践!
答案 2 :(得分:5)
这是一个更简单的方法,它使用一个简单的getter和setter来将字符串编组为枚举。
答案 3 :(得分: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());
}
}
答案 4 :(得分: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>