JSF 2.0: utiliser les valeurs Enum pour selectOneMenu [dupliquer]
cette question a déjà une réponse ici:
- comment utiliser les valeurs enum dans f: selectItem (s) 4 réponses
j'utilise JSF 2.0 et je veux remplir un selectOneMenu avec les valeurs de mon Enum. Un exemple simple:
// 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;
}
}
Malheureusement, je ne peux pas utiliser Couture pour mon projet actuel, qui avait une belle étiquette <s:convertEnum/>
qui a fait la plupart du travail.
En couture, pour utiliser les valeurs de L'Enum, j'ai dû écrire le markup suivant (et créer une usine qui fournit le #{genderValues}
:
<!-- the Seam way -->
<h:selectOneMenu id="persongender" value="#{person.gender}">
<s:selectItems var="_gender" value="#{genderValues}"" label="#{_gender.label}"/>
<s:convertEnum/>
</h:selectOneMenu>
le résultat est que je n'ai pas à déclarer les valeurs Enum explicitement plus à l'intérieur du markup. Je sais que ce n'est pas très facile dans JSF <2.0, mais y a-t-il du nouveau dans JSF2 pour aider avec cette question? Ou est-ce que Souder aide ici d'une façon ou d'une autre? Si il n'y a rien de nouveau dans JSF2, Quelle est la façon la plus facile de le faire dans JSF 1.2?
ou est-ce que je peux même intégrer la balise JSF Seam et les classes correspondantes de Seam pour obtenir la même fonctionnalité dans une JavaEE6-App (sans le conteneur Seam)?
5 réponses
Ok, voici le chemin final: - Enregistrez le convertisseur enum standard dans faces-config.xml (facultatif):
<converter>
<converter-for-class>java.lang.Enum</converter-for-class>
<converter-class>javax.faces.convert.EnumConverter</converter-class>
</converter>
ajouter une fonction par exemple à une fève gérée qui convertit les valeurs Enum en un tableau de Sélectitems:
@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;
}
}
puis lier cette fonction au selectOneMenu dans JSF:
<h:selectOneMenu id="gender" value="#{person.gender}">
<!-- use property name not method name -->
<f:selectItems value="#{genderBean.genderValues}" />
</h:selectOneMenu>
C'est ça! Pas la première explication de ce problème sur le net. Mais je pense que c'est le plus simple & le plus court ;)
après avoir regardé mon propre exemple de couture pendant une minute, j'ai créé une méthode dans un bean managé comme ceci:
@ManagedBean
public class MyManagedBean {
public Gender[] getGenderValues() {
return Gender.values;
}
}
et dans mon markup j'ai mis
<h:selectOneMenu id="gender" value="#{person.gender}">
<f:selectItems value="#{myManagedBean.genderValues}" var="g"
itemValue="#{g}" itemLabel="#{g.label}"/>
</h:selectOneMenu>
maintenant je vais devoir voir si le enum
est sauvegardé correctement dans mon entity quand le formulaire est envoyé. Je vais voir si je peux le faire moi - même-de toute façon, j'apprécierais des conseils ou des pratiques exemplaires à ce sujet!
Voici une méthode plus simple qui utilise un simple getter et setter pour agiter les cordes aux énums.
j'ai rencontré ce problème il y a quelque temps et je l'ai résolu comme vous l'avez fait, mais ensuite j'ai réalisé à un certain point que je ne pouvais pas utiliser i18n avec cette solution parce que les cordes étaient codées dur dans la classe enum. J'ai donc modifié mon énumconverter pour utiliser messages
pour le rendu.
parfois aussi, vous pouvez vouloir rendre l'enum comme un identifiant unique et non comme un texte lisible par l'utilisateur (pour un usage interne à l'intérieur de certains composants).
C'est mon convertisseur:
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());
}
}
j'utilise cette approche simple, il est assez optimiste, vous pouvez le personnaliser pour votre propre but. J'ai mis le code suivant dans un haricot réutilisable, qui peut être appelé à partir de votre application à tout moment, de sorte que vous pouvez utiliser n'importe lequel de vos énums déclarés dans votre paquet.
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);
}
Maintenant l'utiliser dans le fichier xhtml comme suit:
<p:selectOneMenu value="#{jobapp.aplicant.marital}">
<f:selectItems value="#{rtutil.fromEnum('com.company.package.enMarital')}" var="m" itemLabel="#{m}" itemValue="#{m}"/>
</p:selectOneMenu>