Как использовать значения перечисления в f:selectItem(s)

Я хочу сделать раскрывающийся список selectOneMenu, чтобы я мог выбрать статус по моему вопросу. Можно ли сделать f:selectItem более гибким, учитывая, что произойдет, если изменится порядок перечислений и если список будет большим? И мог бы я сделать это лучше? И можно ли автоматически «выбрать» элемент, который есть в вопросе?

Класс перечисления

public enum Status {
    SUBMITTED,
    REJECTED,
    APPROVED
}

Сущность вопроса

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

person LuckyLuke    schedule 22.11.2011    source источник


Ответы (4)


JSF имеет встроенный преобразователь для enum, так что это должно делать:

@Named
@ApplicationScoped
public class Data {

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

}

с участием

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

(примечание: начиная с JSF 2.0 больше нет необходимости указывать SelectItem[] или List<SelectItem>, также принимаются T[] и List<T>, и вы можете получить доступ к текущему элементу по атрибуту var)

Если вы используете служебную библиотеку JSF OmniFaces, вы можете использовать <o:importConstants> вместо компонента.

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

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

Если вы намерены также контролировать метки, вы можете добавить их в перечисление Status:

public enum Status {

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

    private String label;

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

    public String getLabel() {
        return label;
    }

}

с участием

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

Или, что лучше, сделайте значение перечисления ключом свойства локализованного пакета ресурсов (требуется EL 3.0):

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

с этим в файле свойств, связанном с пакетом ресурсов #{text}

data.status.SUBMITTED = Submitted
data.status.REJECTED = Rejected
data.status.APPROVED = Approved
person BalusC    schedule 22.11.2011
comment
Одна вещь, BalusC, можно ли выбрать/просмотреть статус вопроса по умолчанию (например, когда вы редактируете вопрос, вы уже установили статус вопроса на что-то) - person LuckyLuke; 22.11.2011
comment
В приведенном выше примере JSF сделает это по умолчанию, когда #{bean.question.status} имеет допустимое значение перечисления. Вам не нужно ничего делать, кроме как убедиться, что question имеет предварительно заполненное свойство состояния. - person BalusC; 22.11.2011
comment
@BalusC Как получить доступ к порядковому номеру из JSF? - person jacktrades; 15.02.2013
comment
Если, как и я, вы получаете исключение числового формата для += status, попробуйте использовать .concat(status), как предлагает @Ziletka. - person whistling_marmot; 20.05.2016
comment
Если вы предпочитаете java.util.List, вы можете просто изменить тип возврата getStatuses() на List‹Status› и вернуть Arrays.asList(Status.values()); - person stakahop; 04.10.2016
comment
Из-за наследования (в моем случае) JSF не может определить целевой тип как перечисление. Есть ли способ заставить JSF преобразовать значение? - person thomas.mc.work; 01.03.2017
comment
Если я хочу управлять метками элементов selectOneMenu с помощью чего-то вроде: <f:selectItems value="#{receiptController.allPaymentTypes()}" var="paymentType" itemValue="#{paymentType}" itemLabel="#{msg[paymentType.i18nKey]}" /> от msg — это мой пакет сообщений, а i18nKey — ключ, набранный в верхнем регистре, в моем пакете сообщений. - person Roland; 20.09.2017
comment
Как так получилось, что <f:selectItems value="#{Status}" /> звонит Status.values(). Почему этого достаточно и почему вы предпочитаете его <f:selectItems value="#{Status.values()}" />. Спасибо :-) - person Fabian Barney; 20.06.2018
comment
@FabianBarney Щелкните ссылку за ‹o:importConstants›, чтобы узнать о его работе. - person BalusC; 20.06.2018
comment
На этой странице я не нашел ответа на свой вопрос. Кроме того, я не нашел ответа, почему вы предпочитаете <f:selectItems value="#{Status}" /> <f:selectItems value="#{Status.values()}" />. Меня просто интересовали ваши личные предпочтения и почему вы выбираете тот или иной способ. Без проблем. - person Fabian Barney; 20.06.2018

Для локализации мы также можем использовать это решение:

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>

Таким образом, путь к ресурсам для строк локализации не жестко запрограммирован в Enum.

person sasynkamil    schedule 05.04.2012
comment
Обратите внимание, что этот синтаксис поддерживается только с относительно новой версии EL 2.2. В противном случае вы всегда можете взять <c:set> или <ui:param> или создать собственную функцию EL. - person BalusC; 06.04.2012
comment
Спасибо BalusC. Можно ли как-то заменить #{data.statuses} классом enum без использования поддерживающего компонента (например, value=#{org.myproject.Status.values})? - person sasynkamil; 06.04.2012
comment
@BalusC, ты уверен? Я использую GF 3.1.2 (Mojarra JSF 2.1.6). - person sasynkamil; 06.04.2012

Вы можете использовать <f:selectItems value="#{carBean.carList}" /> и вернуть список экземпляров SelectItem, которые обертывают перечисление (используйте Status.values(), чтобы получить все возможные значения).

person Thomas    schedule 22.11.2011

Вы можете использовать следующую служебную функцию el, чтобы получить значения перечисления и использовать их, например, в SelectOneMenu. Нет необходимости создавать bean-компоненты и стандартные методы.

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

Зарегистрируйте его как функцию el в файле 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>

И, наконец, назовите это так:

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

Как и в ответе BalusC, вы должны использовать пакет ресурсов с локализованными метками перечисления, а для более чистого кода вы также можете создать функцию, например getEnumLabel(enum)

person djmj    schedule 13.02.2016
comment
Нет необходимости в функции (подробнее о методе), когда вы можете использовать #{myBundle[enumName.i18nKey]}, а затем поместить ключи i18n в свое перечисление в качестве свойств: BLA_TYPE("SOME_BLA_TYPE_KEY") на BLA_TYPE — это перечисление, которое будет использоваться, а SOME_BLA_TYPE_KEY — это ключ i18n. - person Roland; 20.09.2017