So I am creating a web application which should display a dropdown box with all values from an enum. I found this question which does provide all the info I would need. Sadly, I can't use the code like this, as it throws errors.
First, the enum:
public enum Status {
YES("Yes"), NO("No"), OPT_IN("Opt in");
private String label;
Status(String label) {
this.setLabel(label);
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
}
And now the xhtml page:
<p:importEnum type="package.name.Status"/>
<p:outputLabel for="statusDropdown" value="Status:" />
<p:selectOneMenu id="statusDropdown"
value="#{model.status}" >
<f:selectItems value="#{Status}" var="statusEnum"
itemValue="#{statusEnum}" itemLabel="#{statusEnum.label}"/>
</p:selectOneMenu>
According to the question I linked, a dropdown with the enum values and the label string should show. However, this is not the case.
With itemLabel usage, I get an error saying that 'label' is not a valid enum value.
Abbreviated stacktrace:
java.lang.NumberFormatException: For input string: "label"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at javax.el.ArrayELResolver.coerce(ArrayELResolver.java:144)
at javax.el.ArrayELResolver.getValue(ArrayELResolver.java:61)
at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:176)
at com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:203)
at org.apache.el.parser.AstValue.getValue(AstValue.java:169)
at org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:190)
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:109)
at javax.faces.component.UIComponentBase$AttributesMap.get(UIComponentBase.java:2427)
at org.primefaces.renderkit.SelectRenderer.createSelectItem(SelectRenderer.java:161)
at org.primefaces.renderkit.SelectRenderer.getSelectItems(SelectRenderer.java:114)
And when removing the 'itemLabel' I get another error:
javax.faces.convert.ConverterException: Status: '[Lpackage.name.Status;#42b3ca25' must be convertible to an enum.
at javax.faces.convert.EnumConverter.getAsString(EnumConverter.java:219)
at org.primefaces.renderkit.SelectRenderer.getOptionAsString(SelectRenderer.java:203)
at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeOption(SelectOneMenuRenderer.java:591)
at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeSelectItems(SelectOneMenuRenderer.java:554)
at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeHiddenSelect(SelectOneMenuRenderer.java:241)
at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeInput(SelectOneMenuRenderer.java:207)
I have debugged this error and found that besides the 3 enum values, it tries to add a 4th value. This 4th value is 'ALL_VALUES' array.
In a last test I just changed <p:selectOneMenu> to <h:selectOneMenu> and now it does kind of work. It still adds an ALL_VALUES selection option which it just shouldn't do.
Any help is appreciated to get a dropdown with just the enum values and preferably the correct label as well.
Thanks
The list of imported enums, from Primefaces importEnum component, is managed by ImportEnumTagHandler class.
As you can see in getEnumValues function, it iterates over Enum Constants
of each types and then add in the map another value, with the list of each type, using the suffix specified in the xhtml (default value is ALL_VALUES).
So, if you have only one enum and you don't specify any suffix, you can use the enum for the selectItems like this:
<p:importEnum type="package.Status" />
<p:outputLabel for="statusDropdown" value="Status:" />
<p:selectOneMenu id="statusDropdown" value="#{bean}">
<f:selectItems value="#{Status.ALL_VALUES}"
itemLabel="#{element.label}" itemValue="#{element}" var="element" />
</p:selectOneMenu>
Import enum docs
Related
I'm converting from RichFaces to PrimeFaces 11.0 and replacing a custom component based on the old rich:suggestionBox with p:autoComplete. This works perfectly in most code where the value refers to a POJO property. However I'm finding difficulty getting p:autoComplete to set the value of a reference from an ArrayList of POJOs.
class OrderItem {
...
public List<Contact> getContacts() {
return contacts;
}
...
}
On a view, we want to be able to allow autoCompletes on multiple contacts per orderItem. The orderItem.contacts list is prepopulated with null placeholders by the time this is rendered:
<ui:repeat id="assignabilityContacts" value="#{orderItem.contacts}" var="_contact" varStatus="idx">
...
<p:autoComplete value="#{orderItem.contacts[idx.index]}"
id="existingContact"
binding="#{contactGroupManager.contactAutoComplete}"
converter="#{baseEntityConverter}"
completeMethod="#{contactGroupManager.autocomplete}"
forceSelection="true"
var="row" itemValue="#{row}" itemLabel="#{row.toString()}"
minQueryLength="2"
maxResults="10"
>
<f:attribute name="orderItem" value="#{orderItem}" />
</p:autoComplete>
The signature of the complete method is...
public List<Contact> autocomplete(Object suggest) {
}
And the converter follows Bauke Scholtz pattern for entities:
Implement converters for entities with Java Generics
As defined above, the value of orderItem.contacts[idx.index] is being set to a string, corresponding to the toString() of the selected pojo. The converter is never called.
If I change the value to a property of class Contact on another backing bean, the converter is called and the property is set to the selected Contact entity. This is the correct behavior.
value="#{contactSelector.selectedContact}"
Is it possible that autoConverter is not picking up the entity class from the List, and are there any suggested workarounds for this?
I would like to create a p:selectOneMenu item where the values and selected value display translated strings. So if the user has switched their language to french, the dropdown contents and selection will all be displayed in french, etc.
Currently I have a list of values to display, each of which knows what their string value is in each language (I realize this is probably an antipattern, will refactor once this is working.)
I've solved the issue of displaying the drop down elements in translated fashion using the following code. I've also created a subclass of the Omnifaces SelectItemsConverter class to get a translated string of each object.
<p:selectOneMenu
value="#{linkagecontroller.selectedLink}"
converter="linkageTypeSelectItemsConverter"
var="lnk">
<f:selectItems value="#{linkagecontroller.linkageTypes}"
var="item"/>
<p:column>
<div class="link-item-#{lnk.linkageTypeId}">
<h:outputText value="#{lnk.getKeyValue(language.localeCode)}" />
</div>
</p:column>
</p:selectOneMenu>
LinkageTypeSelectItemsConverter
#FacesConverter("linkageTypeSelectItemsConverter")
public class LinkageTypeSelectItemsConverter extends SelectItemsConverter implements Serializable {
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
final String locale = context.getApplication().evaluateExpressionGet(context,
"#{language.localeCode}",
String.class);
if (value instanceof LinkageTypeKey) {
return ((LinkageTypeKey) value).getKeyValue(locale);
} else {
return super.getAsString(context, component, value);
}
}
}
When the form is displayed, I can debug this converter, it's returning values translated, but the selected element ID is always being displayed in the default language, english. Thoughts?
The converter is for converting item values, not item labels.
You need to explicitly specify itemLabel.
<p:selectOneMenu
value="#{linkagecontroller.selectedLink}"
converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{linkagecontroller.linkageTypes}" var="item"
itemValue="#{item}" itemLabel="#{item.getKeyValue(language.localeCode)}" />
</p:selectOneMenu>
This is only a somewhat strange approach of localization. You usually put translations in a resource bundle instead of in the model itself. It could even be done without a converter if it's an enum.
See also:
How to use enum values in f:selectItem(s)
Localizing enum values in resource bundle.
Here's a snippet of the form I'm trying to submit:
<h:outputText value="Employees"></h:outputText>
<h:selectManyListbox id="employees"
value="#{lookupControl.memberEmployees}"
converter="#{employeeConverter}">
<f:selectItems value="#{lookupControl.employees}"
var="emp"
itemLabel="#{emp.EMPLOYEE_NUMBER}"
itemValue="#{emp}"/>
</h:selectManyListbox>
<h:message id="employeesMsg"
for="employees"
errorStyle="color:red; display:block"
styleClass="errorMessage"/>
<h:outputText value="Project Lead"></h:outputText>
<h:selectOneListbox id="projectLead"
value="#{lookupControl.chosenLead}"
converter="#{employeeConverter}">
<f:selectItems value="#{lookupControl.employees}"
var="emp"
itemLabel="#{emp.EMPLOYEE_NUMBER}"
itemValue="#{emp}"/>
</h:selectOneListbox>
<h:message id="projectLeadMsg"
for="projectLead"
errorStyle="color:red; display:block"
styleClass="errorMessage"/>
And here's the converter employeeConverter:
#FacesConverter(value = "employeeConverter")
public class EmployeeConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// hack to get an em
HelperBean helper = FacesContext.getCurrentInstance().getApplication().evaluateExpressionGet(context, "#{helperBean}", HelperBean.class);
EntityManager em = helper.getEm();
System.out.println(value);
Employee tmp = DBHelper.findEmployee(em, value);
return tmp;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
Employee tmp = (Employee) value;
return tmp.getEMPLOYEE_NUMBER();
}
}
The idea being that a "Project" (the object being created when this form is submitted) has a list of members (List<Employee>) and a team leader (Employee). When I try submitting the form, I get a message along the lines of:
Conversion Error setting value 'dbaccess.persistence.Employee[id=66666666]' for 'null Converter'.
It's saying it can't convert from a String to an Employee, but it worked for the field right above it. I'm a little confused.
The "null converter" is the exception that the converter instance cannot be found. Since you're referencing the converter as a managed bean by converter="#{employeeConverter}, it would only be found if it's annotated with #javax.faces.bean.ManagedBean
Add #ManagedBean(name = "employeeConverterBean") into class EmployeeConverter
#ManagedBean(name = "employeeConverterBean")
#FacesConverter(value = "employeeConverter")
public class EmployeeConverter implements Converter {
and use converter="#{employeeConverterBean}" instead of converter="#{employeeConverter}"
You are referring to a scoped variable in your converter= attribute, but #FacesConverter does not create a scoped variable. Rather, it registers your converter class with the converter ID you specify.
From the documentation for FacesConverter.value:
The value of this annotation attribute is taken to be the converter-id …
Remove the converter= attribute entirely from your h:selectManyListbox, and instead, add a nested f:converter element:
<h:selectManyListbox id="employees"
value="#{lookupControl.memberEmployees}">
<f:converter converterId="employeeConverter"/>
<f:selectItems value="#{lookupControl.employees}"
var="emp"
itemLabel="#{emp.EMPLOYEE_NUMBER}"
itemValue="#{emp}"/>
</h:selectManyListbox>
Update: I'd always taken the tag documentation literally, which says the attribute's value "must evaluate to javax.faces.convert.Converter". BalusC points out that a nested f:converter is not needed; passing a literal converter ID in the converter= attribute will work:
<h:selectManyListbox id="employees"
value="#{lookupControl.memberEmployees}"
converter="employeeConverter">
<f:selectItems value="#{lookupControl.employees}"
var="emp"
itemLabel="#{emp.EMPLOYEE_NUMBER}"
itemValue="#{emp}"/>
</h:selectManyListbox>
I had never felt comfortable doing that in the past, without a documented guarantee that it works. I just looked through the JSF specification and found this under its "Standard HTML RenderKit Tag Library" section:
The following action must be taken to handle the value of the converter property. If isLiteralText() on the converter property returns true, get the value of the property and treat it as a converterId by passing it as the argument to the createConverter() method of the Application instance for this webapp, then pass the created Converter to the setConverter() method of the component for this tag.
So yes, passing a converter-id in the converter= attribute is completely supported.
This question already has answers here:
How to populate options of h:selectOneMenu from database?
(5 answers)
Closed 6 years ago.
I want to pick a custom object from select one menu. It neither shows an error nor values. What should I do?
My xhtml document:
<h:panelGrid columns="2">
<p:outputLabel value="" />
<p:selectOneMenu id="CurrencyMenu" value="#{CurrencyMB.currency}" >
<f:selectItem itemLabel="-- Select Currency--" itemValue="#{null}"/>
<f:selectItems value="#{CurrencyMB.currencyList}" var="currency" itemValue="#{currency.currencyId}" itemLabel="#{currency.currencyName}" >
</f:selectItems>
<p:ajax update="currencyOut" />
</p:selectOneMenu>
<p:outputLabel value="Currency Id : #{CurrencyMB.currency.currencyId}" id="currencyOut" />
</h:panelGrid>
My managedBean class:
#ManagedBean(name = "CurrencyMB")
#RequestScoped
public class CurrencyManagedBean {
private Currency currency;
private List<Currency> currencyList;
public Currency getCurrency() {
return currency;
}
public void setCurrency(Currency currency) {
this.currency = currency;
}
public List<Currency> getCurrencyList() {
currencyList = new ArrayList<Currency>();
currencyList.addAll(getiCurrencyService().getCurrencies());
return currencyList;
}
public void setCurrencyList(List<Currency> currencyList) {
this.currencyList = currencyList;
}
}
You are trying to map a Java object of class Currency to a string that comes as a HTTP request parameter. A converter is intended to be used in a situation when you need to create an object from a its string representation, and vice versa, like in the situation you faced.
Basically there are two approaches.
1. Utilize converter.
With this approach you define item value as a Currency object and use a converter to create string representation from an object and recreate an object back from a string. For the converter part, just follow the tutorial Luiggi pointed at. Basically you need to create a class that implements Converter, annotate it with #FacesConverter("currencyConverter") to be able to refer to the converter by id, like in converter="currencyConverter" attribute of a JSF tag:
<p:selectOneMenu id="CurrencyMenu" value="#{CurrencyMB.currency}" converter="currencyConverter">
<f:selectItems value="#{CurrencyMB.currencyList}" var="currency" itemValue="#{currency}" itemLabel="#{currency.currencyName}" />
<p:ajax update="currencyOut" />
</p:selectOneMenu>
2. Utilize plain Strings (or java primitive wrappers).
With this approach you bind item values, as well as user selection to a bean property of String type, and not to an actual object. Using it this way you won't need any converter, and string values will be set for you:
<p:selectOneMenu id="CurrencyMenu" value="#{CurrencyMB.currencyName}">
<f:selectItems value="#{CurrencyMB.currencyList}" var="currency" itemValue="#{currency.currencyName}" itemLabel="#{currency.currencyName}" />
<p:ajax update="currencyOut" />
</p:selectOneMenu>
Finally, it is worth reading the question to the answer Why selectOneMenu Send ItemLabel to the converter?.
You can create Converter for your Custom Object Currency.
Step 1: Create a Converter class and Implement javax.faces.convert.Converter Interface,Override getAsObject and getAsString methods and write your logic for String to Object Conversion and Object to String Conversion.
Step 2: Simply declare something like #FacesConverter("currencyConverter") in your converter class or If you want use Spring Inject or Autowired Annotation in Converter class declare your Converter Class with #Component("currencyConverter") Annotation and don't use #FacesConverter.And your converter class should in component scan package.
Step 3: Declare your converter in Selectonemenu Using converter property.
If you still have any problem please refer this link
http://www.techpages.org/jsf/jsf-custom-object-converter-from-selectonemenu/2428/
I have a selectOneMenu that loads a list of BD, debugging can ve that all steps are correct except the time it shows the value in selectOneMenu. On page appears the object name "JanelaPortabilidade # 7437" and not like the String.
My codes:
public List<SelectItem> listarHoraJanela(DateSelectEvent event) {
dataF = event.getDate();
Calendar dataAux = new GregorianCalendar();
dataAux.setTime(dataF);
//Corrigir
if (listaHoraJanela == null) {
listaHoraJanela = new ArrayList<JanelaEAPortabilidade>();
listaHoraJanela = consultaJPortabilidade.listarHoraJanela(dataAux);
listSelectHoraJ.add(new SelectItem("", ""));
for (JanelaEAPortabilidade horaJ : listaHoraJanela) {
String dataFormat = String.format("%02d",horaJ.horIniJanela.get(Calendar.HOUR_OF_DAY));
System.out.println(dataFormat);//So here is ok, print in console correct string
listSelectHoraJ.add(new SelectItem(horaJ.getIdtJanela(),dataFormat));
}
}
xhtml:
<h:outputText value="Hora da Janela:" for="horaJanela" />
<h:selectOneMenu value="#{bean.codHoraJanela}" id="horaJanela"
rendered="#{bean.type == 'Fixo'}" >
<f:selectItems value="#{bean.listaHoraJanela}" />
What can it be? I must be missing something.
Thank you!
What you need to do is to use the itemLabel and itemValue attributes for f:selectItems. Here is an example from the PrimeFaces website (http://www.primefaces.org/showcase-labs/ui/selectOneMenu.jsf from the pojo section of the code):
<f:selectItems value="#{autoCompleteBean.players}" var="player" itemLabel="#{player.name}" itemValue="#{player}"/>
The itemLabel is what will be displayed in the drop down list and itemValue is what will be returned (in #{bean.codHoraJanela} in your example).
I'm surprised the code even works. If I didn't miss something the code should throw a NullPointerException. In the JSF you posted a DateSelectEvent isn't created, instead the method is called without any arguments. The DateSelectEvent in the Java method should therefor be NULL.
The output you see is the result of an object's toString() method. Probably the object represented there is coming from the codHoraJanela method in the managed bean. Fix the listarHoraJanela method to return a list of objects.