2010-05-19 10 views
27

Я использую JSF 2.0 и хочу заполнить selectOneMenu значениями моего Enum. Простого пример: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; 
    } 
} 

К сожалению, я не могу использовать Шов для моего текущего проекта, который имел хороший <s:convertEnum/> тега, который сделал большую часть работы. В пластах, чтобы использовать значения 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 < 2.0, но есть ли какие-либо новые в JSF2, чтобы помочь в этом вопросе? Или Weld поможет здесь как-то? Если в JSF2 нет ничего нового, что самый простой способ сделать это в JSF 1.2?

Или я могу даже интегрировать тег JSF Seam и соответствующие классы Seam в g et те же функции в JavaEE6-приложении (без контейнера Seam)?

+4

Смотрите также [Как использовать перечисление в selectOneMenu] (http://stackoverflow.com/q/8229638) и [Как используйте перечисление в selectManyMenu] (http://stackoverflow.com/q/3822058) – BalusC

ответ

46

Ok, вот окончательный путь: - Регистрация стандартное перечисление конвертер в лицах-config.xml (по желанию):

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

Добавить функцию, например в управляемом компоненте, который преобразует значения 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; 
    } 
} 

Затем связать эту функцию к selectOneMenu в JSF:

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

Вот и все! Не первое объяснение этой проблемы в сети. Но я думаю, что это самый простой & самый короткий:)

+43

Регистрация преобразователя не требуется. – BalusC

22

Посмотрев на собственном примере Шов на минуту я создал метод в управляемом компоненте, как это:

@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 сохранен правильно в моей сущности при отправке формы. Я посмотрю, смогу ли я сделать это сам - во всяком случае, я был бы признателен за советы или рекомендации по этому поводу!

+1

Прохладный. Это здорово. – GGrec

4

Я столкнулся с этим вопросом некоторое время назад, и я решил его так же, как и вы, но потом я понял, что с этим решением я не мог использовать i18n, потому что строки были жестко закодированы в классе enum. Поэтому я изменил свой 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

Я использую этот простой подход, весьма оптимистично, вы можете настроить его для собственных целей. Я помещаю следующий код в многоразовый компонент, который может быть вызван из вашего приложения в любое время, поэтому вы можете использовать любое из ваших перечислений, объявленных в вашем пакете.

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>