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/
Related
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.
Given the following XHTML code (regarding PrimeFaces 5.0 final). New changes to data table filters can be visible here.
<p:dataTable id="dataTable" var="row" value="#{testManagedBean}"
lazy="true"
rowKey="#{row.transporterId}"
widgetVar="dataTableUIWidget">
<p:column id="id" headerText="Id" sortBy="#{row.transporterId}">
<h:outputText value="#{row.transporterId}"/>
</p:column>
<p:column id="transporter" headerText="Transporter" filterBy="#{row.transporterName}">
<f:facet name="filter">
<p:inputText onkeyup="PF('dataTableUIWidget').filter();"/>
</f:facet>
<h:outputText value="#{row.transporterName}"/>
</p:column>
</p:dataTable>
And the following is the faces converter to trim leading and trailing spaces from a string.
#FacesConverter(forClass=String.class)
public final class StringTrimmer implements Converter
{
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return value != null ? value.trim() : null;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return (String) value;
}
}
Can this converter be applied globally to all filters of type string (including other UI components which is already applied) as specified in the data table?
<f:facet name="filter">
<p:inputText onkeyup="PF('dataTableUIWidget').filter();"/>
</f:facet>
Or I have to modify the converter class to have the annotations like,
#ManagedBean
#RequestScoped
public final class StringTrimmer implements Converter
{
//...
}
and then apply this converter manually to all filters in question like so,
<f:facet name="filter">
<p:inputText onkeyup="PF('dataTableUIWidget').filter();" converter="#{stringTrimmer}"/>
</f:facet>
Can this converter somehow be applied globally so that we don't need to explicitly specify converter="#{stringTrimmer}" for all string type filters?
The question should be more related to JSF rather than PrimeFaces.
It's caused because the UIInput#getValue() defaults to Object, not String. As long as you don't explicitly bind the value attribute of an UIInput based component to a backing bean property of a more specific type, like String, no specific converter will be looked up either.
It should work if you change
<p:inputText onkeyup="PF('dataTableUIWidget').filter();"/>
to e.g.
<p:inputText value="#{bean.filter}" onkeyup="PF('dataTableUIWidget').filter();"/>
with a private String filter property (and a getter+setter). But this is indeed clutter if you don't use this property anywhere else in the model.
The alternative is indeed explicitly declaring a converter via the converter attribute. According to #FacesConverter contract, it's not possible to simultaneously declare both a converter ID and a converter for-class on the same converter class like so
#FacesConverter(value="stringTrimmer", forClass=String.class)
public final class StringTrimmer implements Converter {
// ...
}
Only the converter ID would be registered and a warning will be printed to the server log.
WARNING: #FacesConverter is using both value and forClass, only value will be applied.
But, it is possible to have both #ManagedBean and #FacesConverter on the same class. You should only understand that they don't cooperate with each other and that completely independent instances would be created. But this shouldn't harm if the converter is designed to be stateless (i.e. all state is kept within the method block and the class doesn't have any instance variables/dependencies).
#ManagedBean
#ApplicationScoped
#FacesConverter(forClass=String.class)
public final class StringTrimmer implements Converter {
// ...
}
This way you can keep having the benefit of forClass and still be able to reference the converter as a managed bean via #{stringTrimmer} on those components where forClass can't apply.
<p:inputText onkeyup="PF('dataTableUIWidget').filter();" converter="#{stringTrimmer}" />
An alternate to #BalusC answer is to bind the property into a generic map using a helper "getter / setter" wrapper object to not clutter your code with boilerplate bean properties.
PropertyAccessor
public class PropertyAccessor<T> implements Serializable
{
// bean property path
private String property;
// value
private T value;
// getter & setter for value
}
Filter map
Save this map in a request or view scoped bean or in your "DAO" wrapping class.
private final Map<String, PropertyAccessor> filterProperties = new HashMap<>();
/**
* Get default / temporary filter property
*
* #param property Name of property
* #param init Initial default value
*
* #return
*/
public <T> PropertyAccessor<T> getFilterProperty(final String property, final T init)
{
if (!this.filterProperties.containsKey(property))
this.filterProperties.put(property, new PropertyAccessor<T>(property, init));
return this.filterProperties.get(property);
}
Example:
A composite component with a generic dao and a statefull filter with converter and optional default value.
<p:column headerText="Barcode" filterBy="#{tblVarProduct.barcode}>
<f:facet name="filter">
<p:inputText value="#{cc.attrs.dao.getFilterProperty('barcode', null).value}"
converter="BarcodeConverter"
onchange="PF('#{tblProductWidgetVar}').filter();"/>
</f:facet>
<h:outputText value="#{tblVarProduct.barcode}"/>
</p:column>
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)
Iam new to JSF technology, currently in our project we are using JSF 2.0 with spring and hibernate integration.I have one doubt regarding h:selectOneMenu and f:selectItems.
From the Database i'm getting a list of UserBeans.I'm using like
<h:selectOneMenu id="users" value="#{MyBean.user}">
<f:selectItems value="#{MyBean.userList}" var="user" itemLabel="#{user.userName}" itemValue="#{user.userId}" />
</h:selectOneMenu>
Here user is of type UserBean and userList is the list of UserBeans.
In the view page it is showing correctly but in the backing bean when i select one item and click on submit button, it showing the selected user as NULL.
My doubt is i can only pass List of SelectItem objects for the f:selectItems or any other beans list ..??
Is there any other way to populate the list of UserBeans otherthan SelectItem for selectItems.
Thank you All,
Anil
You have set the user ID as value of the select items, but you seem to be trying to bind the value to a fullworthy User property in the bean. You need to bind it as an user ID.
<h:selectOneMenu id="users" value="#{MyBean.userId}">
<f:selectItems value="#{MyBean.userList}" var="user" itemLabel="#{user.userName}" itemValue="#{user.userId}" />
</h:selectOneMenu>
If your sole intent is to be able to select and set User instead of only the ID, then you need a Converter to convert between User and String. That's because HTTP/HTML doesn't understand Java objects. It only understands strings. Here's a kickoff example:
<h:selectOneMenu id="users" value="#{MyBean.user}">
<f:selectItems value="#{MyBean.userList}" var="user" itemLabel="#{user.userName}" itemValue="#{user}" />
</h:selectOneMenu>
with
#FacesConverter(forClass=User.class)
public class UserConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
try {
return userService.findById(Long.valueOf(value));
} catch (SomeException e) {
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to User", value)), e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return String.valueOf(((User) value).getId());
}
}
However, this is a pretty expensive job. I'd suggest to stick to passing ID around and obtain the real user in the bean's action method just once instead of letting the converter do it for every single item.