Format date in <f:selectItem(s) itemLabel> using DateTimeConverter - jsf

I have a <h:selectOneMenu> that has <f:selectItems> with CategoryHistory objects loaded in it. I only show the Date date field as itemLabel.
That works but I want to format the date:
I created a converter that extends javax.faces.convert.DateTimeConverter and change the fields in the constructor. But my dates only show in default format :(
DateAndTimeConverter.java
import javax.faces.bean.ManagedBean;
import javax.faces.convert.Converter;
import javax.faces.convert.DateTimeConverter;
import javax.faces.convert.FacesConverter;
#FacesConverter(value = "dateAndTimeconverter")
#ManagedBean
public class DateAndTimeConverter extends DateTimeConverter implements Converter {
public DateAndTimeConverter(){
this.setDateStyle("short");
}
xhtml
<h:selectOneMenu valueChangeListener="#{admin.categoryHistoryListener}"
onchange="submit()" value="#{admin.categoryHistory.id}" converter="#{dateAndTimeconverter}">
<f:selectItems value="#{admin.categoryHistories}" var="n"
itemValue="#{n.id}" itemLabel="#{n.date}">
</f:selectItems>
</h:selectOneMenu>
It also doesn't work when I try:
<h:selectOneMenu valueChangeListener="#{admin.categoryHistoryListener}"
onchange="submit()" value="#{admin.categoryHistory.id}">
<f:converter converterId="dateAndTimeconverter"/>
<f:selectItems value="#{admin.categoryHistories}" var="n"
itemValue="#{n.id}" itemLabel="#{n.date}">
</f:selectItems>
</h:selectOneMenu>
CategoryHistory Has a Date date, and Long id +...
Thank you

Unfortunately, the JSF converters only applies on the input value, not on the input label.
You'll need to solve this other ways. E.g. a getter which uses SimpleDateFormat to format the date. Or if your environment supports EL 2.2, simply invoke the converter method directly (you've it as managed bean already):
<f:selectItems value="#{admin.categoryHistories}" var="n" itemValue="#{n.id}"
itemLabel="#{dateAndTimeconverter.getAsString(facesContext, component, n.date)}">
If you happen to use JSF utility library OmniFaces, then you can also use its of:formatDate() function. E.g.:
<f:selectItems value="#{admin.categoryHistories}" var="n" itemValue="#{n.id}"
itemLabel="#{of:formatDate(n.date, 'd MMM yyyy')}">

You can use a converter method in your bean, as:
public class Admin{
...
public String formatDate(Date fecha, String pattern) {
return (new SimpleDateFormat(pattern)).format(fecha);
}
...
}
And, in your xhtml page inside f:selectItems:
<f:selectItems value="#{admin.categoryHistories}" var="n"
itemValue="#{n.id}" itemLabel="#{admin.formatDate(n.date,'d MMM yyyy')}">
</f:selectItems>

Example
xhtml
<h:selectOneMenu value="#{tbMonitoreoController.fechaMonitoreo}">
<f:selectItems value="#{tbMonitoreoController.fechasMonitoreo}" />
Method in tbMonitoreoController
public SelectItem[] getFechasMonitoreo(){
Collection<Date> entities = getEjbFacade().getFechasMonitoreo();
return JsfUtil.getSelectItemsFechasMonitoreo(entities, true);
}
public static SelectItem[] getSelectItemsFechasMonitoreo(Collection<Date> listDate, boolean selectOne) {
int size = selectOne ? (listDate.size() + 1) : listDate.size();
SelectItem[] items = new SelectItem[size];
int i = 0;
if (selectOne) {
items[0] = new SelectItem(null, "---");
i++;
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
for (Date x : listDate) {
items[i++] = new SelectItem(x, simpleDateFormat.format(x));
}
return items;
}

Related

Primefaces: Form fields not synced with Managed Bean automatically

Have a nice day. I don't know if i'm wrong about this. I have a form in my xhtml like this:
<p:outputLabel value="Número de pasajeros" />:
<p:inputText value="#{vueloMB.instancia.numPasajeros}" maxlength="3" >
</p:inputText>
<br />
<p:outputLabel value="Hora de salida" />:
<p:calendar value="#{vueloMB.instancia.fechaHoraSalida}" navigator="true"
mode="popup" pattern="dd/MM/yyyy HH:mm" />
<br />
<p:outputLabel value="Avión" />:
<p:selectOneMenu value="#{vueloMB.instancia.avion}" >
<f:selectItems value="#{vueloMB.aviones}" var="avi"
itemLabel="#{avi.modelo}" itemValue="#{avi}" />
</p:selectOneMenu>
<br />
<p:outputLabel value="Pais de salida" />:
<p:selectOneMenu value="#{vueloMB.instancia.paisSalida}" converter="omnifaces.SelectItemsConverter" >
<f:selectItems value="#{vueloMB.paises}" var="pai"
itemLabel="#{pai.nombre}" itemValue="#{pai}" />
<f:param name="tipoPais" value="S"></f:param>
<p:ajax update="ciusal" listener="#{vueloMB.cargarListaCiudades}" process="#this" >
</p:ajax>
</p:selectOneMenu>
<br />
<p:outputLabel value="Ciudad de salida" />:
<p:selectOneMenu value="#{vueloMB.instancia.ciudadSalida}" converter="omnifaces.SelectItemsConverter"
id="ciusal" disabled="#{vueloMB.instancia.paisSalida==null}" >
<f:selectItems value="#{vueloMB.ciudadesSalida}" var="ciu"
itemLabel="#{ciu.nombre}" itemValue="#{ciu}" />
</p:selectOneMenu>
<br />
<p:commandButton value="Guardar" rendered="#{vueloMB.instancia.id == null}" action="#{vueloMB.guardar()}" process="#form" ajax="true" />
</h:form>
The dropdown labeled "Ciudad de salida" refreshes another dropdown after i choose a country here, updates the list that feeds the second dropdown and it works fine. The problem is when i press the "Guardar" button to save the entity (vueloMB.instancia is my entity) with JPA, because it doesn't do anything.
So, i added the attribute immediate="true" to the button, it calls the ManagedBean method, but when i see the entity, only the field vueloMB.instancia.paisSalida isn't null, even if i fill all the fields. Because of that, i assumed that, because the dropdown calls an MB method because it refresh the second dropdown, it's value is refreshed on the MB. Based on that, i modified the first field like this:
<p:inputText value="#{vueloMB.instancia.numPasajeros}" maxlength="3" >
<p:ajax />
</p:inputText>
I added the ajax tag to my inputText. After doing that, i press the "Guardar" button and the field that i've modified (Número de pasajeros) now it carries the value on vueloMB.instancia.numPasajeros.
So, if i add to all my fields, when i press the submit button it will work, it will save the entity without problems and all the fields will travel to the managed bean, but is necessary to do that with every field? There's no automatic way JSF does this? Or i have something wrong with my code?
EDIT: Here is the code of the managed bean. A CDI Managed Bean with #ConversationScoped:
package com.saplic.fut.beans;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import com.saplic.fut.daos.VueloDAO;
import com.saplic.fut.entity.Avion;
import com.saplic.fut.entity.Ciudad;
import com.saplic.fut.entity.Pais;
import com.saplic.fut.entity.Vuelo;
#Named("vueloMB")
#ConversationScoped
public class VueloManagedBean implements Serializable {
private static final long serialVersionUID = -203436251219946811L;
#Inject
private VueloDAO vueloDAO;
#Inject
private Conversation conversation;
#PostConstruct
public void iniciarConversacion() {
if(conversation.isTransient())
conversation.begin();
}
public void finalizarConversacion() {
if(!conversation.isTransient())
conversation.end();
}
private Vuelo instancia;
private List<Vuelo> vuelos;
private List<Avion> aviones = new ArrayList<Avion>();
private List<Pais> paises = new ArrayList<Pais>();
private List<Ciudad> ciudadesSalida = new ArrayList<Ciudad>();
private List<Ciudad> ciudadesAterrizaje = new ArrayList<Ciudad>();
private Integer idVuelo;
public String cargarLista() {
iniciarConversacion();
vuelos = vueloDAO.cargarVuelos();
return "/vuelos/lista";
}
public void cargarListaCiudades() {
String tipoLista = FacesContext.getCurrentInstance().
getExternalContext().getRequestParameterMap().get("tipoPais");
if(tipoLista.equalsIgnoreCase("S"))
setCiudadesSalida(vueloDAO.cargarCiudades(getInstancia().getPaisSalida()));
if(tipoLista.equalsIgnoreCase("A"))
setCiudadesAterrizaje(vueloDAO.cargarCiudades(getInstancia().getPaisAterrizaje()));
}
public String cargarDetalle() {
Vuelo fltVuelo = new Vuelo();
fltVuelo.setId(getIdVuelo());
instancia = vueloDAO.cargarDetalle(fltVuelo);
if(instancia == null)
setInstancia(new Vuelo());
//Cargamos lista de aviones para combo
setAviones(vueloDAO.cargarAviones());
setPaises(vueloDAO.cargarPaises());
return "/vuelos/detalle";
}
public String guardar() {
vueloDAO.guardar(instancia);
finalizarConversacion();
return cargarLista();
}
public String actualizar() {
vueloDAO.actualizar(instancia);
finalizarConversacion();
return cargarLista();
}
public String eliminar() {
vueloDAO.eliminar(instancia);
finalizarConversacion();
return cargarLista();
}
public Vuelo getInstancia() {
return instancia;
}
public void setInstancia(Vuelo instancia) {
this.instancia = instancia;
}
public List<Vuelo> getVuelos() {
return vuelos;
}
public void setVuelos(List<Vuelo> vuelos) {
this.vuelos = vuelos;
}
public Integer getIdVuelo() {
return idVuelo;
}
public void setIdVuelo(Integer idVuelo) {
this.idVuelo = idVuelo;
}
public List<Avion> getAviones() {
return aviones;
}
public void setAviones(List<Avion> aviones) {
this.aviones = aviones;
}
public List<Pais> getPaises() {
return paises;
}
public void setPaises(List<Pais> paises) {
this.paises = paises;
}
public List<Ciudad> getCiudadesSalida() {
return ciudadesSalida;
}
public void setCiudadesSalida(List<Ciudad> ciudadesSalida) {
this.ciudadesSalida = ciudadesSalida;
}
public List<Ciudad> getCiudadesAterrizaje() {
return ciudadesAterrizaje;
}
public void setCiudadesAterrizaje(List<Ciudad> ciudadesAterrizaje) {
this.ciudadesAterrizaje = ciudadesAterrizaje;
}
}
Regards.
Your entities must implements the method equals() hashCode() and toString as specified in the omnifaces showcase. I can't help you much more than that since I'm not familiar with omnifaces and the ConversationScope. I think it's because the two objects are not at the same place in memory so when you use equals the result is false. In the case of omnifaces I read it uses toString() to see if two objects are equal so if the method is not reimplemented you will have different results.
In other words you have null values because when the value as string comes back from the form it cannot be converted back to the original object. I'd appreciate if someone could attest this as I'm not 100% positive that's what is happening.

Convert Date on SelectManyCheckbox Selection

i have a simple problem but i didnot find a solution for it.
I have a simple p:selectCheckboxMenu and i want use the selectedDates after click on the button.
I tried it with f:convertDateTime
<h:form id="mainform">
<p:panelGrid columns="2">
<p:selectCheckboxMenu label="Date" value="#{myBean.selectedDates}">
<f:selectItems value="#{myBean.dates}" var="date" itemValue="#{date}" itemLabel="#{myBean.convertDate(date)}"/>
<f:convertDateTime type="date" pattern="dd-MM-yyyy"/>
</p:selectCheckboxMenu>
<p:commandButton value="Test" actionListener="#{myBean.printDates}"/>
</p:panelGrid>
but than i get an Error- Message: "Invaild Value".
Than i tried a Converter:
#FacesConverter("myDateConverter")
public class MyDateConverter extends DateTimeConverter{
public MyDateConverter(){
setPattern("MM/dd/yyyy");
}}
and
<p:selectCheckboxMenu label="Date" value="#{myBean.selectedDates}" converter="myDateConverter">
But same error message. When i use no converter i get "String"- Values in my Date-List because type erasure.
Question: How i get the selected dates as dates?
Here is my bean for completeness:
#ManagedBean(name = "myBean")
#ViewScoped
public class MyBean implements Serializable {
private List<Date> dates;
private List<Date> selectedDates;
private SimpleDateFormat dateFormat;
#PostConstruct
public void init() {
System.out.println("POST CONSTRUCT!");
dateFormat = new SimpleDateFormat("yyyy.MM.dd");
dates = new ArrayList<Date>();
dates.add(new Date());
}
/**
*
*/
public void printDates(){
for(Date d : selectedDates){
System.out.println(d);
}
}
/**
*
* #param date
* #return
*/
public String convertDate(Date date){
return dateFormat.format(date);
}
The converter is the source of the problem as it removes the time and then it uses the default time when converting back to Date.
You can use
< f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss.SSS Z" />
or try with a < f:datetimeconverter >

How to create a JSF converter for XMLGregorianCalendar

I have created a client for my SOAP web service using JSF and RichFaces. Below is my view:
<h:form>
<h:panelGrid id="panel" width="80%" columns="2" columnClasses="col1,col2">
<rich:panel>
<h:outputLabel value="Application Name " />
<h:selectOneMenu value="#{userComplaintBean.appName}">
<f:selectItem itemValue="1" itemLabel="Select" />
<f:selectItem itemValue="App1" itemLabel="App1" />
<f:selectItem itemValue="App2" itemLabel="App2" />
<f:selectItem itemValue="App3" itemLabel="App3" />
<f:selectItem itemValue="App4" itemLabel="App4" />
<f:selectItem itemValue="App5" itemLabel="App5" />
</h:selectOneMenu>
<br />
<h:outputLabel value="Complaint Description " />
<h:inputTextarea value="#{userComplaintBean.complaintDesc}" />
<br />
<h:outputLabel value="Date Expected "/>
<rich:calendar datePattern="yyyy/MM/dd" />
<br/>
<h:commandButton value="submit" action="#{userComplaintBean.save()}" />
</rich:panel>
</h:panelGrid>
</h:form>
Below is my managed bean:
#ManagedBean(name = "userComplaintBean")
#RequestScoped
public class UserComplaintBean {
UserComplaintVO userComplaintVO;
UserComplaintWS userComplaintWS;
UserComplaintWSImplService userComplaintWSImplService;
private int id;
private String appName;
private String complaintDate;
private String complaintDesc;
private Date tentativeDate;
public XMLGregorianCalendar getComplaintDate() throws DatatypeConfigurationException {
XMLGregorianCalendar xgc = null;
GregorianCalendar gc;
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
complaintDate = dateFormat.format(date);
gc = (GregorianCalendar) GregorianCalendar.getInstance();
gc.setTime(date);
xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
return xgc;
}
public void setComplaintDate(String complaintDate) {
this.complaintDate = complaintDate;
}
/*
public XMLGregorianCalendar getTentativeDate() throws DatatypeConfigurationException {
XMLGregorianCalendar xgc = null;
GregorianCalendar gc;
String td;
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
td = dateFormat.format(tentativeDate);
gc = (GregorianCalendar) GregorianCalendar.getInstance();
gc.setTime(tentativeDate);
xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
return xgc;
}
public void setTentativeDate(Date tentativeDate) {
this.tentativeDate = tentativeDate;
}
*/
public String save() throws DatatypeConfigurationException {
userComplaintWSImplService = new UserComplaintWSImplService();
userComplaintWS = userComplaintWSImplService.getUserComplaintWSImplPort();
UserComplaintVO userComplaintVO = new UserComplaintVO();
userComplaintVO.setAppName(getAppName());
userComplaintVO.setComplaintDate(getComplaintDate());
userComplaintVO.setComplaintDesc(getComplaintDesc());
//userComplaintVO.setTentativeDate(getTentativeDate());
userComplaintWS.userComplaintMethod(userComplaintVO);
System.out.println("Complaint Saved...");
return "Success";
}
}
Here I am taking complaintDate from <rich:calendar> which I need to convert to XMLGregorianCalendar format and I am not able to do it.
How can I do the abovementioned conversion?
You're basically making a major design mistake. You shouldn't be mingling a SOAP-specific model into your JSF-specific model. The <rich:calendar> takes a java.util.Date. You should design your model in such way that you provide exactly what the view expects. You should do the SOAP-specific model conversion only afterwards, in the business service method during processing the JSF form submit as preparation for the SOAP request.
Thus, ideally you should be using:
private Date copmlaintDate; // +getter+setter
with
<rich:calendar value="#{userComplaintBean.complaintDate}" />
and then in save() method
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(complaintDate);
XMLGregorianCalendar xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
But if you have really a hard head in for some reason, then you could always hack it around as follows, given that your environment supports EL 2.2 (your action method syntax confirms that this is the case):
private XMLGregorianCalendar copmlaintDate; // +getter (no setter necessary!)
with
<rich:calendar value="#{userComplaintBean.complaintDate.toGregorianCalendar().time}" />
Otherwise, you could always add a new getter, returning the concrete java.util.Calendar instance:
public Calendar getComplaintDateAsCalendar() {
return complaintDate.toGregorianCalendar();
}
with
<rich:calendar value="#{userComplaintBean.complaintDateAsCalendar.time}" />
Although I don't fully understand the reason behind using an XMLGregorianCalendar for keeping the date instances instead of the good old java.util.Date, the way to go is to create your own #FacesConverter that will do the desired transformation for you. Also, beware of doing business logic / performing potentially lengthy calculations in the getter methods that you're doing righ now. One of the ways of achieving that is to extend the JSF-builtin DateTimeConverter.
The kickoff Converter example is provided next:
#FacesConverter("XMLGregorianCalendarConverter")
public class XMLGregorianCalendarConverter extends DateTimeConverter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if(value == null || value.equals("")) {
return null;
}
Date date = super.getAsObject(context, component, value);
GregorianCalendar gc = new GregorianCalendar();
cg.setTime(date);
XMLGregorianCalendar xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
if(xgc == null) {
throw new ConverterException(new FacesMessage("Error converting to XMLGregorianCalendar."));
}
return xgc;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (!(value instanceof XMLGregorianCalendar) || (value == null)) {
return null;
}
Date date = ((XMLGregorianCalendar)value).toGregorianCalendar().getTime();
return super.getAsString(context, component, date);
}
}

Two different SelectItems return single selected value

I have a country class:
public class Country{
private Long id;
private String name;
}
and a person class that has two Country fields
public class Person{
private Country nationality;
private Country nationality2;
}
Now in JSF I use <f:selectItems> to return list of countries to select nationalities as following:
<h:form id="form1">
<h:selectOneMenu value="#{mybean.person.nationality.id}">
<f:selectItems value="#{mybean.countryList}" var="var" itemValue="#{var.id}"/>
</h:selectOneMenu>
<h:selectOneMenu value="#{mybean.person.nationality2.id}">
<f:selectItems value="#{mybean.countryList}" var="var" itemValue="#{var.id}"/>
</h:selectOneMenu>
<p:commandButton actionListener="#{mybean.save}" update="sometable #form"/>
</h:form>
Now the weird problem is that when I submit the form the value assigned to the second field (nationality2) is assigned to both nationality and nationality2 regardless of what has been selected for the first field. For example if the selected value for nationality is 1 and the selected value for nationality2 is 2, when I submit the form both fields have the value 2. Why is this occuring?
PS: JSF implementation is Mojarra 2.1.3
Your concrete problem is caused because you're setting copies of the same Country reference as selected value and then manipulating only the id property. All changes made in one reference get reflected in all other references as well.
E.g.
Country country = new Country();
person.setNationality1(country);
person.setNationality2(country);
country.setId(1); // Gets reflected in both nationalities!
You'd better set the whole Country entity as value instead of manipulating its properties. Create a Converter which converts between Country and id:
#FacesConverter(forClass=Country.class)
public class CountryConverter implements Converter {
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return (value instanceof Country) ? ((Country) value).getId() : null;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
if (!value.matches("\\d+")) {
throw new ConverterException(new FacesMessage("Invalid country ID: " + value));
}
Long countryId = Long.valueOf(value);
MyBean myBean = context.getApplication().evaluateExpressionGet(context, "#{myBean}", MyBean.class);
for (Country country : myBean.getCountries()) {
if (countryId.equals(country.getId())) {
return country;
}
}
throw new ConverterException(new FacesMessage("Unknown country ID: " + value));
}
}
and use it as follows:
<h:selectOneMenu value="#{mybean.person.nationality1}">
<f:selectItems value="#{mybean.countries}" var="country" itemValue="#{country}" itemLabel="#{country.name}" />
</h:selectOneMenu>
<h:selectOneMenu value="#{mybean.person.nationality2}">
<f:selectItems value="#{mybean.countries}" var="country" itemValue="#{country}" itemLabel="#{country.name}" />
</h:selectOneMenu>
Try to give your Second selectOneMenu another name for var. For Example:
<h:selectOneMenu value="#{mybean.person.nationality2.id}">
<f:selectItems value="#{mybean.countryList}" var="var2" itemValue="#{var2.code}"/>
</h:selectOneMenu>
Otherwise you overwrite your changes in the (first) variable var by changing the second selectOneMenu.
EDIT:
If it does not solve the problem, try for tests to create a second countryList (countryList2) and attach it to the second selectOneMenu.
If the value still stays the same, the failure must be in the getters or setters.

How to use enum values in f:selectItem(s)

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

Resources