h:selectOneMenu with values from an enum becomes null after reload - jsf

I followed the advice from How to use enum values in f:selectItem(s) to build a h:selectOneMenu that takes the values of an enum. This works fine until I reload the page.
If I reload the page (Strg+R) what a user could do, the #{bean.relationship} property becomes null. All other properties, such as strings, numbers, etc. remain as they were before (Bean is #ViewScoped and #ManagedBean).
Here is the code from the JSF:
<h:selectOneMenu value="#{bean.relationship}">
<f:selectItems value="#{bean.relationshipTypes}"
var="types" itemValue="#{types}" itemLabel="#{types}" />
</h:selectOneMenu>
<h:inputText value="#{bean.name}" />
Here the code from the Enum:
public enum RelationshipType {
Family,
Friend
}
Here the code from the Bean:
private RelationshipType relationship; // plus getter & setter
private RelationshipType[] relationshipTypes;
private String name; // plus getter & setter
public RelationshipType[] getRelationshipTypes() {
return RelationshipType.values();
}
The enum is part of a larger entity. For ease of display a shortend version. Any idea?
#Geinmachi: Yet, other values stored in the bean are still there after the reload (e.g. name). So, why not for the property related to the enum?

I have used enums for data types
Enum
public enum Datatypes {
INT("Int"), FLOAT("Float"), DOUBLE("Double"), STRING("String"), DATE("Date"), DATETIME(
"DateTime"), BLOB("Blob");
private final String dataType;
private Datatypes(String dataType) {
this.dataType = dataType;
}
public String getDataType() {
return this.dataType;
}
#Override
public String toString() {
return dataType;
}
}
Bean
private List<Datatypes> datatypes = Arrays.asList(Datatypes.values());
public List<Datatypes> getDatatypes() {
return datatypes;
}
public void setDatatypes(List<Datatypes> datatypes) {
this.datatypes = datatypes;
}
xhtml
<p:selectOneMenu value="#{bean.dataType}">
<f:selectItem itemLabel="Select Data type"/>
<f:selectItems value="#{bean.dataTypes}" />
</p:selectOneMenu>
it working for me.

Related

JSF 2.1 selectoneMenu with custom converter

what I'm trying to achieve is pretty simple:
a selectoneMenu populated using an Array of an Enum.
I defined a custom converter, and i noticed a strange thing:
in the converter's method "getAsString" the parameter value in not an element of the array gestioneFlussiGieRRM.tipiReport but the array itself!
I haven't found anything similar googling for it.
So: what am I doing Wrong?
Thank you in advance.
My Page
<h:selectOneMenu value="#{gestioneFlussiGieRRM.tipo}" id="reportType">
<f:converter converterId ="tipoReport.converter" />
<f:selectItems value="#{gestioneFlussiGieRRM.tipiReport}" var="t" itemValue="#{t}" itemLabel="#{t.descrizione}" />
</h:selectOneMenu>
My Managed bean:
private TipoReport tipo = TipoReport.StorageFacilityReport;
/* getter and setter for tipo */
/** #return the tipiReport */
public TipoReport[] getTipiReport() {
return TipoReport.values();
}
The Enum
public enum TipoReport {
StorageFacilityReport(1, "Storage Facility Report"), StorageParticipantActivityReport(2,
"Storage Participant Activity Report"), StorageUnavailabilityReport(3, "Storage Unavailability Report");
private int tipo;
private String descrizione;
private TipoReport(int tipo, String descrizione) {
this.tipo = tipo;
this.descrizione = descrizione;
}
public int getTipo() {
return tipo;
}
public String getDescrizione() {
return descrizione;
}
}
EDIT:
I'm Using JSF 2.1.27 mojarra implementation

Using the implicit JSF converter javax.faces.convert.EnumConverter explicitly in p:dataTable filters

Enum :
public enum OrderStatus {
New("New"),
Paid("Paid"),
Shipped("Shipped"),
Completed("Completed");
private final String label;
private OrderStatus(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
}
Using enum type in a <p:dataTable> filter as follows.
<p:dataTable var="row"
value="#{testBacking}"
lazy="true"
rows="10"
widgetVar="dataTableUIWidget">
<p:column id="id" headerText="Id">
<h:outputText value="#{row.orderId}"/>
</p:column>
<p:column headerText="Order Status" filterBy="#{row.orderStatus}">
<f:facet name="filter">
<p:selectOneMenu onchange="PF('dataTableUIWidget').filter();">
<f:selectItem itemLabel="Select" itemValue=""/>
<f:selectItems var="orderStatus"
value="#{enumBean.orderStatus}"
itemLabel="#{orderStatus.label}"/>
</p:selectOneMenu>
</f:facet>
<h:outputText value="#{row.orderStatus}"/>
</p:column>
</p:dataTable>
row.orderStatus is a type of the above enum in its associated JPA entity.
The filter associated with the <p:selectOneMenu> needs to specify the javax.faces.convert.EnumConverter explicitly, since its value is not bound to a backing bean property (else an appropriate converter would have appropriately played its role implicitly on its own based on the type of the property in the associated backing bean).
I expect that mentioning a converter as follows
<f:converter converterId="javax.faces.Enum"/>
should work as other implicit converters.
This however, causes an issue (when <f:converter> is specified as said above).
Severe: JSF1006: Cannot instantiate converter of type javax.faces.Enum
What is the issue with this converter? I am seeking for a workable solution.
Using PrimeFaces 5.2 final / Mojarra 2.2.12.
Additional :
The converter specifies javax.faces.Enum as converterId.
public class EnumConverter implements Converter, PartialStateHolder {
public static final String CONVERTER_ID = "javax.faces.Enum";
public static final String ENUM_ID = "javax.faces.converter.EnumConverter.ENUM";
public static final String ENUM_NO_CLASS_ID = "javax.faces.converter.EnumConverter.ENUM_NO_CLASS";
private Class<? extends Enum> targetClass;
private boolean isTransient;
private boolean initialState;
public EnumConverter() {}
}
Therefore, this should work by specifying javax.faces.Enum as convertId of <f:converter> as other implicit JSF converters.
The bean used in the above test-case (completely optional to be peer reviewed) :
#Named
#ViewScoped
public class TestBacking extends LazyDataModel<OrderTable> implements Serializable {
#Inject
private OrderService orderService;
private static final long serialVersionUID = 1L;
public TestBacking() {}
#Override
public List<OrderTable> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
if (MapUtils.isNotEmpty(filters)) { // Debugging purpose only.
System.out.println("Enum filter : " + (filters.get("orderStatus") instanceof OrderStatus));
System.out.println("Filter value : " + filters.get("orderStatus"));
}
int rowCount = MapUtils.isNotEmpty(filters) ? orderService.rowCount(filters).intValue() : orderService.rowCount().intValue();
setRowCount(rowCount);
return orderService.getList(first, pageSize, null, filters);
}
}
The stdout statements inside the load() method display the following output.
Enum filter : false
Filter value : New
(filters.get("orderStatus") instanceof OrderStatus returns false as obvious, since the filter component <p:selectOneMenu> is not passed through the converter. It simply returns String without conversion).
The EnumConverter is a special converter which can only be constructed with a Class<E> as argument (which ultimately get set as targetClass). Without it, the converter won't work. It unfortunately isn't usable across all enums (i.e. it's actually not a "generic enum converter").
There where you assumed implicit/automatic conversion on enums, it's actually done by EL coercion, not by JSF EnumConverter. EL has indeed generic support for enums as long as the target type is resolveable as an enum.
To explicitly use the JSF enum converter, you basically need to extend EnumConverter like below to pass the target enum into c'tor (no need to override the getAsString/Object() methods):
#FacesConverter(value="orderStatusConverter")
public class OrderStatusConverter extends EnumConverter {
public OrderStatusConverter() {
super(OrderStatus.class);
}
}
And then reference it instead:
<f:converter converterId="orderStatusConverter" />
Don't forget to alter <f:selectItem itemValue=""> to be itemValue="#{null}", or you will get a ClassCastException on java.lang.String.
If you happen to use OmniFaces, you can also use its generic enum converter instead:
<f:converter converterId="omnifaces.GenericEnumConverter" />

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 store the data's selected in a selectManyCheckbox into different objects?

In my view I am using StudentModelBean to store the data entered in the form. Consider this part of my form:
<h:selectManyCheckbox value="#{}">
<f:selectItem itemLabel="English" itemValue="English" />
<f:selectItem itemLabel="Hindi" itemValue="Hindi" />
<f:selectItem itemLabel="Telugu" itemValue="Telugu" />
</h:selectManyCheckbox>
My requirement is that I need to store each selected item value into the languageName property of each Languages object. At the end I need to get them in the List object. How can I achieve this?
You'd need to provide the whole Language objects as both the available items and the selected items. You also need to create a Converter which converts between the Language object and String, this is mandatory because HTML output and HTTP request parameters are one and all String.
Assuming that your Language object has two properties code and name and that you've an application scoped bean which look like this:
#ManagedBean
#ApplicationScoped
public class Data {
private List<Language> languages;
#PostConstruct
public void init() {
languages= new ArrayList<Language>();
languages.add(new Language("en", "English"));
languages.add(new Language("hi", "Hindi"));
languages.add(new Language("te", "Telugu"));
// ...
}
public List<Language> getLanguages() {
return languages;
}
}
Then you can use it as follows:
<h:selectManyCheckbox value="#{bean.selectedLanguages}" converter="languageConverter">
<f:selectItems value="#{data.languages}" var="language"
itemValue="#{language}" itemLabel="#{language.name}" />
</h:selectManyCheckbox>
with this bean
#ManagedBean
#ViewScoped
public class Bean {
private List<Language> selectedLanguages;
// ...
}
and this converter
#FacesConverter("languageConverter")
public class LanguageConverter implements Converter {
#Override
public String getAsString(FacesContext context, UIComponent component, Object object) {
return ((Language) object).getCode();
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
List<Language> languages = (List<Language>) context.getApplication().evaluateExpressionGet(context.getELContext(), "#{data.languages}", List.class);
for (Language language : languages) {
if (language.getCode().equals(submittedValue)) {
return language;
}
}
return null;
}
}

Convert h:selectBooleanCheckbox value between boolean and String

I have a backing bean containing a field creditCard which can have two string values y or n populated from the DB. I would like to display this in checkbox so that y and n gets converted to boolean.
How can I implement it? I can't use a custom converter as getAsString() returns String while rendering the response whereas I need a boolean.
The <h:selectBooleanCheckbox> component does not support a custom converter. The property has to be a boolean. Period.
Best what you can do is to do the conversion in the persistence layer or to add extra boolean getter/setter which decorates the original y/n getter/setter or to just replace the old getter/setter altogether. E.g.
private String useCreditcard; // I'd rather use a char, but ala.
public boolean isUseCreditcard() {
return "y".equals(useCreditcard);
}
public void setUseCreditcard(boolean useCreditcard) {
this.useCreditcard = useCreditcard ? "y" : "n";
}
and then use it in the <h:selectBooleanCheckbox> instead.
<h:selectBooleanCheckbox value="#{bean.useCreditcard}" />
You can use the BooleanConverter for java primitives, this parses the text to boolean in your managedbean, at here just put in your code like this in you .xhtml file
<p:selectOneMenu id="id"
value="#{yourMB.booleanproperty}"
style="width:60px" converter="javax.faces.Boolean">
<p:ajax listener="#{yourMB.anylistener}"
update="anyIDcontrol" />
<f:selectItem itemLabel="------" itemValue="#{null}"
noSelectionOption="true" />
<f:selectItem itemLabel="y" itemValue="true" />
<f:selectItem itemLabel="n" itemValue="false" />
</p:selectOneMenu>
ManagedBean:
#ManagedBean(name="yourMB")
#ViewScoped
public class YourMB implements Serializable {
private boolean booleanproperty;
public boolean isBooleanproperty() {
return booleanproperty;
}
public void setBooleanproperty(boolean booleanproperty) {
this.booleanproperty = booleanproperty;
}
}
I had the similar problem, and I agree with previous post, you should handle this issues in persistence layer.
However, there are other solutions. My problem was next: I have TINYINT column in database which represented boolean true or false (0=false, 1=true). So, I wanted to display them and handle as a boolean in my JSF application. Unfortunately, that was not quite possible or just I didn't find a proper way. But instead using checkbox, my solution was to use selectOneMeny and to convert those values to "Yes" or "No". Here is the code, so someone with similar problem could use it.
Converter:
#FacesConverter("booleanConverter")
public class BooleanConverter implements Converter{
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
short number= 0;
try {
if (value.equals("Yes")) {
number= 1;
}
} catch (Exception ex) {
FacesMessage message = new FacesMessage();
message.setSeverity(FacesMessage.SEVERITY_FATAL);
message.setSummary(MessageSelector.getMessage("error"));
message.setDetail(MessageSelector.getMessage("conversion_failed") + ex.getMessage());
throw new ConverterException(message);
}
return number;
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return value.toString();
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
JSF Page:
<h:selectOneMenu id="selectOnePlayerSucc" value="#{vezbaTrening.izvedenaUspesno}" converter="booleanConverter">
<f:selectItems id="itemsPlayerSucc" value="#{trainingOverview.bool}" var="opt" itemValue="#{opt}" itemLabel="#{opt}"></f:selectItems>
And in my ManagedBean I created a list with possible values ("Yes" and "No")
private List<String> bool;
public List<String> getBool() {
return bool;
}
public void setBool(List<String> bool) {
this.bool = bool;
#PostConstruct
public void init () {
...
bool = new LinkedList<>();
bool.add("Yes");
bool.add("No");
}

Resources