How to use enum values in f:selectItem(s)
Solution 1
JSF has a builtin converter for enum
, so this should do:
@Named
@ApplicationScoped
public class Data {
public Status[] getStatuses() {
return Status.values();
}
}
with
<h:selectOneMenu value="#{bean.question.status}" >
<f:selectItems value="#{data.statuses}" />
</h:selectOneMenu>
(note: since JSF 2.0 there's no need anymore to provide a SelectItem[]
or List<SelectItem>
, a T[]
and List<T>
are accepted as well and you can access the current item by var
attribute)
If you happen to use JSF utility library OmniFaces, then you could use <o:importConstants>
instead of a bean.
<o:importConstants type="com.example.Status" />
<h:selectOneMenu value="#{bean.question.status}" >
<f:selectItems value="#{Status}" />
</h:selectOneMenu>
If you intend to control the labels as well, you could add them to the Status
enum:
public enum Status {
SUBMITTED("Submitted"),
REJECTED("Rejected"),
APPROVED("Approved");
private String label;
private Status(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
}
with
<f:selectItems value="#{data.statuses}" var="status"
itemValue="#{status}" itemLabel="#{status.label}" />
Or, better, make the enum value a property key of a localized resource bundle (EL 3.0 required):
<f:selectItems value="#{data.statuses}" var="status"
itemValue="#{status}" itemLabel="#{text['data.status.' += status]}" />
with this in a properties file associated with resource bundle #{text}
data.status.SUBMITTED = Submitted
data.status.REJECTED = Rejected
data.status.APPROVED = Approved
Solution 2
For localization we can use also this 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>
So the resource path for localization strings are not hardcoded in Enum.
Solution 3
You could use <f:selectItems value="#{carBean.carList}" />
and return a list of SelectItem
instances that wrap the enum (use Status.values()
to get all possible values).
Solution 4
You can use following utility el function to obtain the enum values and use them in a SelectOneMenu
for example. No need to create beans and boilerplate methods.
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;
}
}
Register it as an el function in a taglib file:
<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>
And finally call it like:
<p:selectOneMenu value="#{bean.type}">
<f:selectItems value="#{el:getEnumValues('package.BeanType')}" var="varEnum"
itemLabel="#{el:getEnumLabel(varEnum)}" itemValue="#{varEnum}"/>
</p:selectOneMenu>
Similiar to BalusC answer you should be using a resource bundle with localized enum labels and for cleaner code you can also create a function like getEnumLabel(enum)
Related videos on Youtube
LuckyLuke
Updated on July 08, 2021Comments
-
LuckyLuke almost 3 years
I want to make a selectOneMenu dropdown so I can select a status on my question. Is it possible to make the f:selectItem more flexible considering what happens if the order of the enums changes, and if the list was large? And could I do this better? And is it possible to automatically "select" the item that the question have?
Enum class
public enum Status { SUBMITTED, REJECTED, APPROVED }
Question entity
@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>
-
LuckyLuke over 12 yearsOne thing BalusC, is it possible to "select"/view the status that a question has as default (for example when you are editing a question then you have already set the status of the question to something)
-
BalusC over 12 yearsIn the above example, JSF will do it by default when
#{bean.question.status}
has a valid enum value. You don't need to do anything expect of ensuring that thequestion
has the proper status property prefilled. -
BalusC about 12 yearsNote that this syntax is only supported since EL 2.2 which is "relatively" new. Otherwise you can always grab
<c:set>
or<ui:param>
or homebrew a custom EL function. -
sasynkamil about 12 yearsThank you BalusC. Is it possible to somehow replace #{data.statuses} with enum Class, without using backing bean (e.g. value="#{org.myproject.Status.values}")?
-
sasynkamil about 12 years@BalusC are you sure? I'm using GF 3.1.2 (Mojarra JSF 2.1.6).
-
jacktrades over 11 years@BalusC How to access ordinal value from JSF?
-
whistling_marmot about 8 yearsIf, like me, you get a number format exception for
+= status
, then try using.concat(status)
as @Ziletka suggests. -
stakahop over 7 yearsIf you prefer java.util.List you can just modify getStatuses() return type to List<Status> and return Arrays.asList(Status.values());
-
thomas.mc.work about 7 yearsDue to inheritance (in my case) JSF isn't able to detect the target type as an enum. Is there a way to force JSF to convert the value?
-
Roland over 6 yearsIf I want to control the labels of the
selectOneMenu
's items with something like:<f:selectItems value="#{receiptController.allPaymentTypes()}" var="paymentType" itemValue="#{paymentType}" itemLabel="#{msg[paymentType.i18nKey]}" />
bymsg
is my message bundle andi18nKey
is an all-uppercase key in my message bundle. -
Roland over 6 yearsNo need for a "function" (method more), when you can use
#{myBundle[enumName.i18nKey]}
and then put the i18n keys into your enumeration as properties:BLA_TYPE("SOME_BLA_TYPE_KEY")
byBLA_TYPE
is the enum to be used andSOME_BLA_TYPE_KEY
is the i18n key. -
Fabian Barney almost 6 yearsHow does it happend that
<f:selectItems value="#{Status}" />
is callingStatus.values()
. Why is it sufficient and why do you prefer it over<f:selectItems value="#{Status.values()}" />
. Thanks :-) -
BalusC almost 6 years@FabianBarney Click the link behind <o:importConstants> to learn about its working.
-
Fabian Barney almost 6 yearsI did not find the answer to my question on that page. Furthermore I did not find the answer why you prefer
<f:selectItems value="#{Status}" />
over<f:selectItems value="#{Status.values()}" />
. I was just interested in your personal preferences and why you choose the one way over the other. But no problem.