que jsf java-ee jsf-2 jboss-weld

jsf - que - jboss wikipedia



JSF 2.0: use los valores de Enum para selectOneMenu (5)

Esta pregunta ya tiene una respuesta aquí:

Estoy usando JSF 2.0 y quiero llenar un selectOneMenu con los valores de mi Enum. Un simple ejemplo:

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

Desafortunadamente, no puedo usar Seam para mi proyecto actual, que tenía una buena <s:convertEnum/> que hizo la mayor parte del trabajo. En Seam, para usar los valores de Enum, tuve que escribir el siguiente marcado (y crear una fábrica que proporciona el #{genderValues} :

<!-- the Seam way --> <h:selectOneMenu id="persongender" value="#{person.gender}"> <s:selectItems var="_gender" value="#{genderValues}"" label="#{_gender.label}"/> <s:convertEnum/> </h:selectOneMenu>

El resultado es que ya no tengo que declarar explícitamente los valores de Enum dentro del marcado. Sé que esto no es muy fácil en JSF <2.0, pero ¿hay algo nuevo en JSF2 para ayudar con este problema? ¿O Weld ayuda aquí de alguna manera? Si no hay nada nuevo en JSF2, ¿cuál es la forma más fácil de hacerlo en JSF 1.2?

¿O puedo incluso integrar la etiqueta Seam JSF y las clases correspondientes de Seam para obtener la misma característica en una aplicación JavaEE6 (sin el contenedor Seam)?



Después de mirar mi propio ejemplo de Seam por un minuto, creé un método en un bean administrado como este:

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

Y en mi marcado puse

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

Ahora tendré que ver si la enum se guarda correctamente en mi entidad cuando se envía el formulario. Veré si puedo hacer esto por mí mismo. De todos modos, ¡apreciaría consejos o mejores prácticas sobre esto!


Me encontré con este problema hace un tiempo y lo resolví como lo hiciste, pero luego me di cuenta en algún momento que con esa solución no podía usar i18n porque las cadenas estaban codificadas en la clase enum. Así que modifiqué mi enumConverter para usar messages para renderizar.

También a veces es posible que desee representar la enumeración como un identificador único y no como texto legible por el usuario (para uso interno dentro de algunos componentes).

Este es mi convertidor:

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


Ok, esta es la última forma: - Registra el convertidor enum estándar en faces-config.xml (opcional):

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

Agregue una función, por ejemplo, a un bean administrado que convierta los valores de Enum a una matriz de 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; } }

A continuación, enlace esta función al selectOneMenu en JSF:

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

¡Eso es! No es la primera explicación para este problema en la red. Pero creo que es el más fácil y el más corto;)


Utilizo este enfoque simple, es bastante optimista, puedes personalizarlo para tu propio propósito. Puse el siguiente código en un bean reutilizable, que puede ser llamado desde su aplicación en cualquier momento, para que pueda usar cualquiera de sus enumeraciones declaradas en su paquete.

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

Ahora úsalo en el archivo xhtml de la siguiente manera:

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