JSF 2.0: Используйте значения Enum для Selectonemenu [дубликат
-
01-10-2019 - |
Вопрос
Этот вопрос уже имеет ответ здесь:
Я использую JSF 2.0 и хочу заполнить селевоНемену со значениями моего enum. Простой пример:
// 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, чтобы помочь с этой проблемой? Или съемка помогает здесь как-то? Если в JSF2 нет ничего нового, какой простой способ сделать это в JSF 1.2?
Или я могу даже интегрировать тег шва jsf и соответствующие классы шва, чтобы получить ту же функцию в javaee6-приложении (без контейнера для шва)?
Решение
Хорошо, вот последний способ: - Зарегистрировать стандартный конвертер Enum в Faies-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;
}
}
Затем свяжите эту функцию к селетенемену в JSF:
<h:selectOneMenu id="gender" value="#{person.gender}">
<!-- use property name not method name -->
<f:selectItems value="#{genderBean.genderValues}" />
</h:selectOneMenu>
Вот и все! Не первое объяснение этой проблемы в сети. Но я думаю, что это самый простой и кратчайший;)
Другие советы
Посмотрев на мой собственный пример шва на минуту, я создал метод в управляемом бобовом бобе:
@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
правильно сохраняется в моей сущности, когда форма отправляется. Я посмотрю, смогу ли я сделать это сам - в любом случае, я был бы признателен за советы или лучшие практики на этом!
Вот проще прощественный метод, который использует простую доставку и установку для маршаловных струн в enums.
Я бегаю в эту проблему некоторое время назад, и я решил его, как вы сделали, но тогда я осознал, что я понял, что я с этим решением я не мог использовать I18n, потому что струны были жесткозедированы в классе Enum. Так что я изменил свой Enumconverter, чтобы использовать messages
для рендеринга.
Также иногда вы можете сделать enum как какой-то уникальный идентификатор, а не как читаемый текст пользователя (для внутреннего использования внутри некоторых компонентов).
Это мой преобразователь:
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());
}
}
Я использую этот простой подход, довольно оптимистично, вы можете настроить его для своей цели. Я положил следующий код в многоразовый боб, который можно вызвать из вашего приложения в любое время, поэтому вы можете использовать любой из ваших enum, объявленных в вашем пакете.
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>