Comment utiliser les valeurs d'énumération dans f: selectItem (s)

103

Je souhaite créer une liste déroulante selectOneMenu afin de pouvoir sélectionner un statut sur ma question. Est-il possible de rendre le f: selectItem plus flexible compte tenu de ce qui se passe si l'ordre des énumérations change, et si la liste était grande? Et pourrais-je faire mieux? Et est-il possible de "sélectionner" automatiquement l'élément de la question?

Classe Enum

public enum Status {
    SUBMITTED,
    REJECTED,
    APPROVED
}

Entité de question

@Enumerated(EnumType.STRING)
private Status status;

JSF

<div class="field">
    <h:outputLabel for="questionStatus" value="Status" />
    <h:selectOneMenu id="questionStatus" value="#{bean.question.status}" >
        <f:selectItem itemLabel="Submitted" itemValue="0" />
        <f:selectItem itemLabel="Rejected" itemValue="1" />
        <f:selectItem itemLabel="Approved" itemValue="2" />
    </h:selectOneMenu>
    <hr />
</div>
Lucky Luke
la source

Réponses:

210

JSF a un convertisseur intégré pour enum, donc cela devrait faire:

@ManagedBean
@ApplicationScoped
public class Data {

    public Status[] getStatuses() {
        return Status.values();
    }

}

avec

<h:selectOneMenu value="#{bean.question.status}" >
    <f:selectItems value="#{data.statuses}" />
</h:selectOneMenu>

(note: depuis JSF 2.0, il n'est plus nécessaire de fournir un SelectItem[]ou List<SelectItem>, un T[]et List<T>sont également acceptés et vous pouvez accéder à l'élément actuel par varattribut)

Si vous utilisez la bibliothèque d'utilitaires JSF OmniFaces , vous pouvez utiliser à la <o:importConstants>place d'un bean.

<o:importConstants type="com.example.Status" />

<h:selectOneMenu value="#{bean.question.status}" >
    <f:selectItems value="#{Status}" />
</h:selectOneMenu>

Si vous avez l'intention de contrôler également les étiquettes, vous pouvez les ajouter à l' Statusénumération:

public enum Status {

    SUBMITTED("Submitted"),
    REJECTED("Rejected"),
    APPROVED("Approved");

    private String label;

    private Status(String label) {
        this.label = label;
    }

    public String getLabel() {
        return label;
    }

}

avec

<f:selectItems value="#{data.statuses}" var="status"
    itemValue="#{status}" itemLabel="#{status.label}" />

Ou, mieux, faites de la valeur enum une clé de propriété d'un bundle de ressources localisé (EL 3.0 requis):

<f:selectItems value="#{data.statuses}" var="status"
    itemValue="#{status}" itemLabel="#{text['data.status.' += status]}" />

avec ceci dans un fichier de propriétés associé à l'ensemble de ressources #{text}

data.status.SUBMITTED = Submitted
data.status.REJECTED = Rejected
data.status.APPROVED = Approved
BalusC
la source
Une chose BalusC, est-il possible de "sélectionner" / afficher le statut d'une question par défaut (par exemple, lorsque vous éditez une question, vous avez déjà défini le statut de la question sur quelque chose)
LuckyLuke
Dans l'exemple ci-dessus, JSF le fera par défaut lorsqu'il #{bean.question.status}a une valeur d'énumération valide. Vous n'avez rien à faire pour vous assurer que la questionpropriété a le statut approprié prérempli.
BalusC
@BalusC Comment accéder à la valeur ordinale depuis JSF?
jacktrades
2
Si, comme moi, vous obtenez une exception de format numérique pour += status, essayez d'utiliser .concat(status)comme @Ziletka le suggère.
whistling_marmot
Si vous préférez java.util.List, vous pouvez simplement modifier le type de retour getStatuses () en List <Status> et renvoyer Arrays.asList (Status.values ​​());
stakahop
16

Pour la localisation, nous pouvons également utiliser cette solution:

public enum Status { SUBMITTED, REJECTED, APPROVED }

data.status.SUBMITTED=Submitted
data.status.REJECTED=Rejected
data.status.APPROVED=Approved

<h:selectOneMenu value="#{bean.question.status}" >
    <f:selectItems
        value="#{data.statuses}"
        var="status"
        itemValue="#{status}"
        itemLabel="#{text['data.status.'.concat(status)]}" />
</h:selectOneMenu>

Ainsi, le chemin d'accès aux ressources pour les chaînes de localisation n'est pas codé en dur dans Enum.

sasynkamil
la source
1
Notez que cette syntaxe n'est prise en charge que depuis EL 2.2 qui est "relativement" nouveau. Sinon, vous pouvez toujours saisir <c:set>ou <ui:param>ou créer une fonction EL personnalisée.
BalusC
Merci BalusC. Est-il possible de remplacer en quelque sorte # {data.statuses} par enum Class, sans utiliser de bean de sauvegarde (par exemple value = "# {org.myproject.Status.values}")?
sasynkamil
@BalusC êtes-vous sûr? J'utilise GF 3.1.2 (Mojarra JSF 2.1.6).
sasynkamil
4

Vous pouvez utiliser <f:selectItems value="#{carBean.carList}" />et renvoyer une liste d' SelectIteminstances qui encapsulent l'énumération (à utiliser Status.values()pour obtenir toutes les valeurs possibles).

Thomas
la source
2

Vous pouvez utiliser la fonction suivante de l'utilitaire el pour obtenir les valeurs d'énumération et les utiliser dans un SelectOneMenupar exemple. Pas besoin de créer des haricots et des méthodes standard.

public final class ElEnumUtils
{
    private ElEnumUtils() { }

    /**
     * Cached Enumerations, key equals full class name of an enum
     */
    private final static Map<String, Enum<?>[]> ENTITY_ENUMS = new HashMap<>();;

    /**
     * Retrieves all Enumerations of the given Enumeration defined by the
     * given class name.
     *
     * @param enumClassName Class name of the given Enum.
     *
     * @return
     *
     * @throws ClassNotFoundException
     */
    @SuppressWarnings("unchecked")
    public static Enum<?>[] getEnumValues(final String enumClassName) throws ClassNotFoundException
    {
        // check if already cached - use classname as key for performance reason
        if (ElEnumUtils.ENTITY_ENUMS.containsKey(enumClassName))
            return ElEnumUtils.ENTITY_ENUMS.get(enumClassName);

        final Class<Enum<?>> enumClass = (Class<Enum<?>>) Class.forName(enumClassName);

        final Enum<?>[] enumConstants = enumClass.getEnumConstants();

        // add to cache
        ElEnumUtils.ENTITY_ENUMS.put(enumClassName, enumConstants);

        return enumConstants;
    }
}

Enregistrez-le comme fonction el dans un fichier taglib:

<function>
    <description>Retrieves all Enumerations of the given Enumeration defined by the given class name.</description>
    <function-name>getEnumValues</function-name>
    <function-class>
        package.ElEnumUtils
    </function-class>
    <function-signature>
        java.lang.Enum[] getEnumValues(java.lang.String)
    </function-signature>
</function>

Et enfin, appelez-le comme:

<p:selectOneMenu value="#{bean.type}">
    <f:selectItems value="#{el:getEnumValues('package.BeanType')}" var="varEnum" 
        itemLabel="#{el:getEnumLabel(varEnum)}" itemValue="#{varEnum}"/>
</p:selectOneMenu>

Similaire à la réponse BalusC, vous devriez utiliser un ensemble de ressources avec des étiquettes d'énumération localisées et pour un code plus propre, vous pouvez également créer une fonction comme getEnumLabel(enum)

djmj
la source
Pas besoin de "fonction" (méthode plus), quand vous pouvez utiliser #{myBundle[enumName.i18nKey]}puis mettre les clés i18n dans votre énumération en tant que propriétés: BLA_TYPE("SOME_BLA_TYPE_KEY")by BLA_TYPEest l'énumération à utiliser et SOME_BLA_TYPE_KEYest la clé i18n.
Roland le