Develop Custom JSF Converters

User Rating: 4 / 5

Star ActiveStar ActiveStar ActiveStar ActiveStar Inactive
 

In this tutorial we will show how to create a CDI search-powered Combo box which uses a JSF Converter to render the Objects in the Combo in text format and back into objects for processing.

Populating a combobox with a list of labels and values has been always a time consuming task for every developers. In JSF 1.x you would be forced to wrap item values/labels in ugly SelectItem instances.


<h:selectOneMenu id="selectOneCB" value="#{page.selectedName}">
     <f:selectItems value="#{page.names}"/>
</h:selectOneMenu>

Then on the Managed Bean:

List<SelectItem> names = new ArrayList<SelectItem>();
   //-- Populate list from database
   names.add(new SelectItem(valueObject,"label"));

This is fortunately not needed anymore  since you can used custom ?Converters. ?Converters can be used for several purposes, the most obvious one is automatic conversion of a text format into a required one format, such as data time format
<h:inputText id="date" value="#{bean.date}"
        size="20" required="true"
        label="Enter Date" >

        <f:convertDateTime pattern="d-M-yyyy" />
</h:inputText>

Here's an other example which can be used to force number conversion with a set of parameters:
<h:inputText id="salary" value="#{User.salary}">
             <f:convertNumber maxFractionDigits="2"
                       groupingUsed="true"
                       currencySymbol="$"
                       maxIntegerDigits="7"
                       type="currency"/>
</h:inputText>

On the other hand, a Custom data conversion is necessary if you need to convert field data into an application-specific value object. This can be the typical use case for a Combobox which needs to store data as Object (so that it can be further managed by the application), on the other hand it should be visibile in user-friendly text format.
Let's see an example where a list of Customer Entities is added to a Combobox:

jsf custom converters

  

         <h:panelGrid columns="3" columnClasses="titleCell">
             
                <h:selectOneMenu value="#{requestController.customer}" converter="#{customerIdConverter}">
                   <f:selectItems value="#{requestController.customerList}" var="customer" itemValue="#{customer}" itemLabel="#{customer.name}" />
                </h:selectOneMenu>
                                    
                   </f:selectItems>
                </h:selectOneMenu>


            </h:panelGrid>

And here's the relevant part from the RequestController Bean:


@Model
public class RequestController {
    
    @Inject
    private ApplicationQuery query;
    
    @PostConstruct
    public void initNewRequest() {
        quantity = 0;
        customerList = query.findAllOrderedByName();
    }
    
    private Customer customer;
    
    private List<Customer> customerList;
    
    public Customer getCustomer() {
        return customer;
    }
    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    public List<Customer> getCustomerList() {
        return customerList;
    }

    public void setCustomerList(List<Customer> customerList) {
        this.customerList = customerList;
    }

    private String name;  
   
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

 // .. Other methods

}

The Converter is a Simple class implementing the javax.faces.convert.Converter interfaces and annotated with the @javax.faces.convert.FacesConverter annotation:


package com.sample.converter;

import javax.enterprise.inject.Model;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;
import javax.inject.Inject;

import com.packtpub.data.ApplicationQuery;
import com.packtpub.model.Customer;
@FacesConverter 
@Model
public class CustomerIdConverter implements Converter {
    @Inject
    private ApplicationQuery query;
     @Override
        public Object getAsObject(FacesContext context, UIComponent component, String value) {
         
            if (value == null || value.isEmpty()) {
                return null;
            }

            try {
                 
                Object obj =  query.findById(Long.valueOf(value));
               
                return obj;
            } catch (Exception e) {
                e.printStackTrace();
                throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to Customer", value)), e);
            }
        }

        @Override
        public String getAsString(FacesContext context, UIComponent component, Object value) {
             
            if (!(value instanceof Customer)) {
                return null;
            }

           String s =  String.valueOf(((Customer) value).getId());
          
           return s;
        }

}

Here, the method Object getAsObject(FacesContext context, UIComponent component, String value) converts the given string value into an object that is appropriate for storage in the given component.
On the other hand the method String getAsString(FacesContext context, UIComponent component, Object value) converts the given object, which is stored in the given component, into a string representation.

This is where the two methods are invoked into the JSF lifecycle:

jsf custom converter

The Entity Customer can be retrieved using an @ApplicationScoped CDI Bean that takes as parameter the id of the customer.
package com.sample;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import com.packtpub.model.Customer;
import com.packtpub.model.Request;

import java.util.List;

@ApplicationScoped
public class ApplicationQuery {

    @Inject
    private EntityManager em;

    public Customer findById(Long id) {
        return em.find(Customer.class, id);
    }


    public List<Customer> findAllOrderedByName() {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Customer> criteria = cb.createQuery(Customer.class);
        Root<Customer> member = criteria.from(Customer.class);

        criteria.select(member).orderBy(cb.asc(member.get("name")));
        return em.createQuery(criteria).getResultList();
    }
}

Hope you enjoyed reading about JSF Converters. Would you like to go on reading about CDI ?

Follow us on Twitter