Jak używać wartości wyliczenia w f: selectItem (s)

103

Chcę utworzyć listę rozwijaną selectOneMenu, aby móc wybrać stan mojego pytania. Czy można uelastycznić f: selectItem, biorąc pod uwagę, co się stanie, jeśli zmieni się kolejność wyliczeń i jeśli lista będzie duża? Czy mógłbym zrobić to lepiej? Czy jest możliwe automatyczne „wybranie” pozycji, której dotyczy pytanie?

Klasa wyliczenia

public enum Status {
    SUBMITTED,
    REJECTED,
    APPROVED
}

Podmiot pytania

@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
źródło

Odpowiedzi:

210

JSF ma wbudowany konwerter dla enum, więc powinno to zrobić:

@ManagedBean
@ApplicationScoped
public class Data {

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

}

z

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

(uwaga: od JSF 2.0 nie ma już potrzeby podawania SelectItem[]lub List<SelectItem>, a T[]i List<T>są również akceptowane, a dostęp do bieżącego elementu można uzyskać za pomocą varatrybutu)

Jeśli korzystasz z biblioteki narzędziowej JSF OmniFaces , możesz użyć <o:importConstants>zamiast fasoli.

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

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

Jeśli chcesz kontrolować również etykiety, możesz dodać je do Statuswyliczenia:

public enum Status {

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

    private String label;

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

    public String getLabel() {
        return label;
    }

}

z

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

Lub lepiej, uczyń wartość wyliczenia kluczem właściwości zlokalizowanego pakietu zasobów (wymagany EL 3.0):

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

z tym w pliku właściwości powiązanym z pakunkiem zasobów #{text}

data.status.SUBMITTED = Submitted
data.status.REJECTED = Rejected
data.status.APPROVED = Approved
BalusC
źródło
Jedną rzeczą BalusC, możliwe jest „wybierz” / wyświetlanie stanu, że pytanie ma domyślnie (na przykład, gdy edytujesz pytanie to już ustawiony status pytanie do czegoś)
Lucky Luke
W powyższym przykładzie JSF zrobi to domyślnie, jeśli #{bean.question.status}ma prawidłową wartość wyliczenia. Nie musisz nic robić, oczekując upewnienia się, że questionwłaściwość statusu została wstępnie wypełniona.
BalusC
@BalusC Jak uzyskać dostęp do wartości porządkowej z JSF?
jacktrades
2
Jeśli, tak jak ja, dostaniesz wyjątek od formatu liczb += status, spróbuj użyć tego, .concat(status)co sugeruje @Ziletka.
whistling_marmot
Jeśli wolisz java.util.List, możesz po prostu zmodyfikować zwracany typ getStatuses () na List <Status> i zwrócić Arrays.asList (Status.values ​​());
stakahop
16

Do lokalizacji możemy wykorzystać również to rozwiązanie:

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>

Tak więc ścieżka zasobów dla ciągów lokalizacji nie jest zakodowana na stałe w Enum.

sasynkamil
źródło
1
Zauważ, że ta składnia jest obsługiwana tylko od wersji EL 2.2, która jest „stosunkowo” nowa. W przeciwnym razie zawsze można chwycić <c:set>lub <ui:param>czy homebrew niestandardową funkcję EL.
BalusC
Dziękuję BalusC. Czy można w jakiś sposób zamienić # {data.statuses} na enum Class, bez używania backing bean (np. Value = "# {org.myproject.Status.values}")?
sasynkamil
@BalusC czy na pewno? Używam GF 3.1.2 (Mojarra JSF 2.1.6).
sasynkamil
4

Możesz użyć <f:selectItems value="#{carBean.carList}" />i zwrócić listę SelectIteminstancji, które zawijają wyliczenie (użyj, Status.values()aby uzyskać wszystkie możliwe wartości).

Tomasz
źródło
2

Możesz użyć następującej funkcji narzędzia el, aby uzyskać wartości wyliczenia i użyć ich SelectOneMenuna przykład. Nie ma potrzeby tworzenia fasoli i metod standardowych.

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

Zarejestruj ją jako funkcję el w pliku 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>

I na koniec nazwij to tak:

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

Podobnie jak odpowiedź BalusC, powinieneś używać pakietu zasobów ze zlokalizowanymi etykietami wyliczeń, a dla bardziej przejrzystego kodu możesz również utworzyć funkcję taką jak getEnumLabel(enum)

djmj
źródło
Nie ma potrzeby stosowania "funkcji" (metoda więcej), kiedy możesz użyć, #{myBundle[enumName.i18nKey]}a następnie umieścić klucze i18n w swoim wyliczeniu jako właściwości: BLA_TYPE("SOME_BLA_TYPE_KEY")by BLA_TYPEjest wyliczeniem, które ma być użyte i SOME_BLA_TYPE_KEYjest kluczem i18n.
Roland