Using custom converter for h:selectOneMenu
In this tutorial we will show you how to convert a select-item of a selectOneMenu or a selectOneListBox into a key and back into the representing object. This is a common issue regarding JSF converters and the h:selectOneMenu
and h:selectOneListBox
components. The purpose of this tutorial is to display many select-items in a dropdown and convert the key back into the representation of that same object. Since JSF doesn’t have any default converters which we can use we must create our own custom converter.
Maven Dependencies
Here are the dependencies required for this project. You can add this via maven or make sure every library is added to your classpath.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.memorynotfound.jsf.converter</groupId>
<artifactId>custom-converter</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>JSF - ${project.artifactId}</name>
<url>https://memorynotfound.com</url>
<packaging>war</packaging>
<dependencies>
<!-- java ee api -->
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<!-- JSF api and impl -->
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.2.12</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.2.12</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
A Simple POJO
Let’s start by describing our simple POJO. We have a Beer bean which has an id and a name.
package com.memorynotfound.jsf;
public class Beer {
private Integer id;
private String brand;
public Beer(Integer id, String brand) {
this.id = id;
this.brand = brand;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
}
Managed Bean
Next we have our managed bean which has a list of beer objects which we want to display on the page.
package com.memorynotfound.jsf;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.SessionScoped;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@ManagedBean
@SessionScoped
public class BeersBean implements Serializable {
private Beer selectedBeer;
private List<Beer> beers;
public BeersBean(){
beers = new ArrayList<Beer>();
beers.add(new Beer(10, "La Chouffe"));
beers.add(new Beer(20, "Stella Artois"));
beers.add(new Beer(30, "Westmalle Trippel"));
}
public Beer getSelectedBeer() {
return selectedBeer;
}
public void setSelectedBeer(Beer selectedBeer) {
this.selectedBeer = selectedBeer;
}
public List<Beer> getBeers() {
return beers;
}
public void setBeers(List<Beer> beers) {
this.beers = beers;
}
public Beer getBeer(Integer id) {
if (id == null){
throw new IllegalArgumentException("no id provided");
}
for (Beer beer : beers){
if (id.equals(beer.getId())){
return beer;
}
}
return null;
}
}
Display select-items using selectOneMenu
We can render our collection of beer objects using the h:selectOneMenu
component. The list will render ok. However, the problem occurs when we choose any beer and we try to populate the selectedBeer property with it. We’ll receive a conversion error. The solution is to add a custom converter which will translate the key into the representing object, as shown next.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Custom converter for h:selectOneMenu</title>
</h:head>
<h:body>
<h2>JSF 2 custom converter for h:selectOneMenu example</h2>
<h:form>
<h:panelGrid columns="2">
<h:outputLabel value="Favorite beer:"/>
<h:selectOneMenu value="#{beersBean.selectedBeer}" converter="beerConverter">
<f:selectItems value="#{beersBean.beers}" var="beer"
itemValue="#{beer}"
itemLabel="#{beer.brand}"/>
</h:selectOneMenu>
<h:commandButton value="submit" action="result"/>
</h:panelGrid>
</h:form>
</h:body>
</html>
Custom Converter
The solution is to write a custom converter. The getAsString
method we extract and return the id of the beer, and in the getAsObject
method, the submitted beer id is converted back into a Beer instance.
package com.memorynotfound.jsf;
import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
@FacesConverter(value = "beerConverter")
public class BeerConverter implements Converter {
@Override
public Object getAsObject(FacesContext ctx, UIComponent uiComponent, String beerId) {
ValueExpression vex =
ctx.getApplication().getExpressionFactory()
.createValueExpression(ctx.getELContext(),
"#{beersBean}", BeersBean.class);
BeersBean beers = (BeersBean)vex.getValue(ctx.getELContext());
return beers.getBeer(Integer.valueOf(beerId));
}
@Override
public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object beer) {
return ((Beer)beer).getId().toString();
}
}
Displaying the result
This code will display the submitted values. We display the selected beer.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Custom converter for h:selectOneMenu</title>
</h:head>
<h:body>
<h2>JSF 2 custom converter for h:selectOneMenu example</h2>
<p>Favorite beer: #{beersBean.selectedBeer.id}, #{beersBean.selectedBeer.brand}</p>
</h:body>
</html>
Demo
URL: localhost:8080/jsf-custom-converter
Thank you! :)
you are super thanks you really better than BALUC
Great explanation and excellent beer choice!
Thank you dude u saved me