Cara menggunakan nilai enum di f: selectItem (s)

103

Saya ingin membuat dropdown selectOneMenu sehingga saya dapat memilih status pertanyaan saya. Apakah mungkin membuat f: selectItem lebih fleksibel dengan mempertimbangkan apa yang terjadi jika urutan enum berubah, dan jika daftarnya besar? Dan dapatkah saya melakukan ini dengan lebih baik? Dan apakah mungkin untuk secara otomatis "memilih" item yang dimiliki pertanyaan?

Kelas enum

public enum Status {
    SUBMITTED,
    REJECTED,
    APPROVED
}

Entitas pertanyaan

@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>
Luke yang beruntung
sumber

Jawaban:

210

JSF memiliki konverter bawaan enum, jadi ini harus dilakukan:

@ManagedBean
@ApplicationScoped
public class Data {

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

}

dengan

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

(catatan: karena JSF 2.0 tidak perlu lagi menyediakan SelectItem[]atau List<SelectItem>, a T[]dan List<T>diterima juga dan Anda dapat mengakses item saat ini berdasarkan varatribut)

Jika Anda kebetulan menggunakan pustaka utilitas JSF OmniFaces , Anda dapat menggunakan <o:importConstants>sebagai pengganti kacang.

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

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

Jika Anda juga bermaksud untuk mengontrol label, Anda dapat menambahkannya ke Statusenum:

public enum Status {

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

    private String label;

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

    public String getLabel() {
        return label;
    }

}

dengan

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

Atau, lebih baik, buat nilai enum sebagai kunci properti dari paket sumber daya yang dilokalkan (diperlukan EL 3.0):

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

dengan ini di file properti yang terkait dengan paket sumber daya #{text}

data.status.SUBMITTED = Submitted
data.status.REJECTED = Rejected
data.status.APPROVED = Approved
BalusC
sumber
Satu hal BalusC, apakah mungkin untuk "memilih" / melihat status yang dimiliki pertanyaan sebagai default (misalnya ketika Anda mengedit pertanyaan maka Anda telah mengatur status pertanyaan menjadi sesuatu)
LuckyLuke
Dalam contoh di atas, JSF akan melakukannya secara default ketika #{bean.question.status}memiliki nilai enum yang valid. Anda tidak perlu melakukan apa pun untuk memastikan bahwa properti questionmemiliki status yang sesuai sebelumnya.
BalusC
@BalusC Bagaimana cara mengakses nilai ordinal dari JSF?
jacktrades
2
Jika, seperti saya, Anda mendapatkan pengecualian format angka += status, coba gunakan .concat(status)seperti yang disarankan @Ziletka.
whistling_marmot
Jika Anda lebih suka java.util.List Anda bisa memodifikasi getStatuses () tipe pengembalian ke List <Status> dan mengembalikan Arrays.asList (Status.values ​​());
stakahop
16

Untuk lokalisasi kita juga bisa menggunakan solusi ini:

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>

Jadi jalur sumber daya untuk string lokalisasi tidak di-hardcode di Enum.

Ziletka
sumber
1
Perhatikan bahwa sintaks ini hanya didukung sejak EL 2.2 yang "relatif" baru. Jika tidak, Anda selalu dapat mengambil <c:set>atau <ui:param>atau mengubah nama fungsi EL kustom.
BalusC
Terima kasih BalusC. Apakah mungkin untuk entah bagaimana mengganti # {data.statuses} dengan enum Class, tanpa menggunakan backing bean (misal value = "# {org.myproject.Status.values}")?
Ziletka
@BalusC apakah kamu yakin? Saya menggunakan GF 3.1.2 (Mojarra JSF 2.1.6).
Ziletka
4

Anda bisa menggunakan <f:selectItems value="#{carBean.carList}" />dan mengembalikan daftar SelectIteminstance yang membungkus enum (gunakan Status.values()untuk mendapatkan semua kemungkinan nilai).

Thomas
sumber
2

Anda dapat menggunakan fungsi utilitas el berikut untuk mendapatkan nilai enum dan menggunakannya dalam a SelectOneMenumisalnya. Tidak perlu membuat metode kacang dan boilerplate.

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

Daftarkan sebagai fungsi el di file 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>

Dan akhirnya menyebutnya seperti:

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

Mirip dengan jawaban BalusC, Anda harus menggunakan paket sumber daya dengan label enum yang dilokalkan dan untuk kode yang lebih bersih Anda juga dapat membuat fungsi seperti getEnumLabel(enum)

djmj
sumber
Tidak perlu "fungsi" (metode lainnya), ketika Anda dapat menggunakan #{myBundle[enumName.i18nKey]}dan kemudian memasukkan kunci i18n ke dalam enumerasi Anda sebagai properti: BLA_TYPE("SOME_BLA_TYPE_KEY")by BLA_TYPEadalah enum yang akan digunakan dan SOME_BLA_TYPE_KEYmerupakan kunci i18n.
Roland