Verwendung von Aufzählungswerten in f: selectItem (s)

103

Ich möchte ein Dropdown-Menü für selectOneMenu erstellen, damit ich einen Status für meine Frage auswählen kann. Ist es möglich, das f: selectItem flexibler zu gestalten, wenn man bedenkt, was passiert, wenn sich die Reihenfolge der Aufzählungen ändert und die Liste groß ist? Und könnte ich das besser machen? Und ist es möglich, das Element der Frage automatisch "auszuwählen"?

Aufzählungsklasse

public enum Status {
    SUBMITTED,
    REJECTED,
    APPROVED
}

Frage Entität

@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
quelle

Antworten:

210

JSF hat einen eingebauten Konverter für enum, also sollte dies tun:

@ManagedBean
@ApplicationScoped
public class Data {

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

}

mit

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

(Hinweis: Seit JSF 2.0 ist es nicht mehr erforderlich, ein SelectItem[]oder List<SelectItem>, ein T[]und List<T>anzugeben, und Sie werden ebenfalls akzeptiert, und Sie können über das varAttribut auf das aktuelle Element zugreifen. )

Wenn Sie zufällig die JSF-Dienstprogrammbibliothek OmniFaces verwenden , können Sie diese <o:importConstants>anstelle einer Bean verwenden.

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

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

Wenn Sie auch die Beschriftungen steuern möchten, können Sie sie der StatusAufzählung hinzufügen :

public enum Status {

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

    private String label;

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

    public String getLabel() {
        return label;
    }

}

mit

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

Oder machen Sie den Enum-Wert besser zu einem Eigenschaftsschlüssel eines lokalisierten Ressourcenpakets (EL 3.0 erforderlich):

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

damit in einer Eigenschaftendatei, die dem Ressourcenpaket zugeordnet ist #{text}

data.status.SUBMITTED = Submitted
data.status.REJECTED = Rejected
data.status.APPROVED = Approved
BalusC
quelle
Eine Sache BalusC, ist es möglich, den Status, den eine Frage als Standard hat, "auszuwählen" / anzuzeigen (wenn Sie beispielsweise eine Frage bearbeiten, haben Sie den Status der Frage bereits auf etwas gesetzt)
LuckyLuke
Im obigen Beispiel führt JSF dies standardmäßig aus, wenn #{bean.question.status}ein gültiger Aufzählungswert vorliegt. Sie müssen nichts tun, um sicherzustellen, dass die questionEigenschaft mit dem richtigen Status vorab ausgefüllt ist.
BalusC
@BalusC Wie greife ich von JSF auf den Ordnungswert zu?
Jacktrades
2
Wenn Sie wie ich eine Ausnahme für das Zahlenformat erhalten += status, versuchen Sie es .concat(status)wie von @Ziletka vorgeschlagen.
whistling_marmot
Wenn Sie java.util.List bevorzugen, können Sie einfach den Rückgabetyp getStatuses () in List <Status> ändern und Arrays.asList (Status.values ​​()) zurückgeben.
Stakahop
16

Zur Lokalisierung können wir auch diese Lösung verwenden:

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>

Daher ist der Ressourcenpfad für Lokalisierungszeichenfolgen in Enum nicht fest codiert.

Ziletka
quelle
1
Beachten Sie, dass diese Syntax nur seit EL 2.2 unterstützt wird, die "relativ" neu ist. Ansonsten kann man immer greifen <c:set>oder <ui:param>eine benutzerdefinierte EL - Funktion oder Homebrew.
BalusC
Vielen Dank, dass Sie BalusC. Ist es möglich, # {data.statuses} irgendwie durch enum Class zu ersetzen, ohne Backing Bean zu verwenden (zB value = "# {org.myproject.Status.values}")?
Ziletka
@ BalusC bist du sicher? Ich verwende GF 3.1.2 (Mojarra JSF 2.1.6).
Ziletka
4

Sie können <f:selectItems value="#{carBean.carList}" />eine Liste von SelectItemInstanzen verwenden und zurückgeben , die die Aufzählung umschließen (verwenden Sie Status.values(), um alle möglichen Werte abzurufen ).

Thomas
quelle
2

Sie können die folgende Funktion des Dienstprogramms el verwenden, um die Aufzählungswerte abzurufen und sie beispielsweise in einem SelectOneMenuzu verwenden. Keine Notwendigkeit, Bohnen und Boilerplate-Methoden zu erstellen.

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

Registrieren Sie es als el-Funktion in einer Taglib-Datei:

<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>

Und nenne es schließlich so:

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

Ähnlich wie bei BalusC sollten Sie ein Ressourcenpaket mit lokalisierten Enum-Labels verwenden. Für saubereren Code können Sie auch eine Funktion wie erstellen getEnumLabel(enum)

djmj
quelle
Keine Notwendigkeit für eine "Funktion" (Methode mehr), wenn Sie #{myBundle[enumName.i18nKey]}die i18n-Schlüssel als Eigenschaften verwenden und dann in Ihre Aufzählung einfügen können: BLA_TYPE("SOME_BLA_TYPE_KEY")by BLA_TYPEist die zu verwendende Aufzählung und SOME_BLA_TYPE_KEYder i18n-Schlüssel.
Roland