JSF Event handling


<< Previous        Next >>


Handling Events in JSF


JSF event model is based on Java Event-Driven programming, which is traditionally available only with desktop GUI applications. If you are familiar with Swing programming, it is easy to understand the event handling model in JSF framework. The basic concept behind the event model in JSF is, the user interface objects such as, button, listbox, menu etc keep a list of listeners. These listeners are notified(listener method is invoked) when the user interface objects generate an event. That is, when user activates a component, an event is fired and listener get notified of the event.
                                   This sounds like a simple straightforward programming pattern, provided the events are created and handled in the same machine. And its true for traditional desktop GUI applications developed using Swing. JSF being a web tier framework, the user action take place in client(browser), that has no permanent connection to the server. So the server will be notified about the event only when next time client makes a connection to the server(eg: submitting a form). To deal with this special senario, JSF defines a strict request processing life cycle.

Java Server Faces technology supports three kinds of events:
  • action events
  • value-change events
  • data-model events.
An action event occurs when the user activates a component like button or hyperlink, that implements ActionSource.

A value-change event occurs when the user changes the value of a component like UIInput, UISelectOne, UISelectMany, and UISelectBoolean components, that implements EditableValueHolder.

Different ways of handling events in JSF
  • Implement a method in a backing bean to handle the event and refer that method with a JSF EL expression from the appropriate attribute of the component
  • Implement an event listener to handle the event and registers the listener on a component by nesting a listener tag inside the UI component tag.
We will explore the event handling in JSF with a simple example. In our previous example, we have created a page with name, age, email and dob field. On clicking submit button,the details will be displayed in the next page. Let's modify the example to include the implementation of event handling in JSF.

We will make the following changes to our previous sample application.
  • User should not be allowed to submit the form, if the Name entered is 'Vampire'
  • User should not be allowed to submit the form, if the Age entered is not correct with respect to the Date of  Birth entered.
  • System should show appropriate error messages in both cases.
  • Add a new button Confirm Age in the GUI, which will allow the user to calculale the age from the Date of Birth.
The page should be rendered as shown below, if there are errors.

                                                                         
Listener as backing bean method

First we will modify the sample by implementing listener as backing bean method

Backing Bean Method That Handles an Action Event

Step 1 : Implementing a Method to Handle an Action Event

A backing bean method that handles an action event must be a public method that accepts an action event and returns void. In our example the method calculateAgeListener is invoked when user clicks Confirm Age button.

public void calculateAgeListener(ActionEvent event) {
       int calculatedAge = calculateAgeFromDOB();
       if (event.getComponent().getId().equals("confirm")) {
             if (calculatedAge != this.age) {
                  this.isAgeCorrected = true;
                  this.output = null;
                  this.age = calculatedAge;
             }
       }
}

Step 2 : Referencing a Method That Handles an Action Event

The method we created in Step 1 referenced with a JSF EL expression from the actionListener attribute of the component. Only components that implement ActionSource can refer to this method. These components include buttons and hyperlinks.

<h:commandButton id="confirm" value="Confirm Age"
       actionListener="#{userDetails.calculateAgeListener}">
</h:commandButton>

Backing Bean Method That Handles a Value-change Event

Step 1 : Implementing a Method to Handle a Value-Change Event

A backing bean method that handles an value change event must be a public method that accepts an value change event and returns void. In our example valueChangeInput is invoked when user changes a value in the name field. When the user enters the name in the text field, a value-change event is generated.

public void valueChangeInput(ValueChangeEvent event){
      if(event.getComponent().getId().equals("name")){
          if(event.getNewValue().equals("Vampire")){
               showerrorMessage=true;
               errorMessage="You are a Vampire";
               output=null;
          }
         else{
               this.output="submitted";
          }
     }
}


Step 2 : Referencing a Method That Handles an Value-Change Event

The method is referenced with a JSF EL expression from the valueChangeListener attribute of the component. Value-change events occur when the user changes the value of a component that implements EditableValueHolder.

<h:inputText id="name" value="#{userDetails.name}" required="true"
         requiredMessage="#{msg.enter_name}"
         validatorMessage="#{msg.enter_name_validlength}"
         valueChangeListener="#{userDetails.valueChangeInput}">
         <f:validateLength minimum="3" />
</h:inputText>

The page look like:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<f:view locale="#{languageDetails.locale}">
<b> <h:outputText value="#{msg.user_details_form}">
</h:outputText> </b>
<p><h:messages style="color: blue" /></p>
<h:form id="userDetailsForm">
         <h:outputText style="color: red"
                rendered="#{userDetails.ageCorrected}"
                value="Your age corrected to #{userDetails.age}">
         </h:outputText>
         <br>
         <h:outputText style="color: red"
                 rendered="#{userDetails.showNameErrorMessage}"
                 value="#{userDetails.nameErrorMessage}">
         </h:outputText>
         <br>
         <h:outputText style="color: red"
                 rendered="#{userDetails.showErrorMessage}"
                 value="#{userDetails.errorMessage}"></h:outputText>
         <h:panelGrid columns="2">
         <h:outputText value="#{msg.name}"></h:outputText>
         <h:inputText id="name" value="#{userDetails.name}" required="true"
                 requiredMessage="#{msg.enter_name}"
                 validatorMessage="#{msg.enter_name_validlength}"
                 valueChangeListener="#{userDetails.valueChangeInput}">
                  <f:validateLength minimum="3" />
         </h:inputText>
         <h:outputText value="#{msg.age}"></h:outputText>
         <h:inputText id="age" value="#{userDetails.age}" required="true"
                 requiredMessage="#{msg.enter_age}"
                 validatorMessage="#{msg.enter_correctage}">
                 <f:validateLength maximum="3" />
         </h:inputText>
         <h:outputText value="#{msg.email}"></h:outputText>
         <h:inputText id="email" value="#{userDetails.email}" required="true"
                 requiredMessage="#{msg.enter_email}"></h:inputText>
         <h:outputText value="#{msg.dob}"></h:outputText>
         <h:inputText id="dob" value="#{userDetails.dob}" required="true"
                 requiredMessage="#{msg.enter_dob}"
                converterMessage="#{msg.enter_dobpattern}">
                <f:convertDateTime type="date" pattern="MM/dd/yyyy" />
         </h:inputText>
         <h:commandButton id="confirm" value="Confirm Age"
                 actionListener="#{userDetails.calculateAgeListener}"></h:commandButton>
         <h:commandButton id="submit" value="#{msg.submit}"
                  action="#{userDetails.submitUserDetails}"></h:commandButton>
         </h:panelGrid>
</h:form>
</f:view>
</body>
</html>

The backing bean look like:

package com.user.details;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;


public class UserDetails {

  private String name;
  private Integer age = null;
  private String email;
  private Date dob;
  // for rendering message in the same page
  private boolean submitted = false;
  private String output;
  private boolean isAgeCorrected = false;
  private String errorMessage;
  private boolean showErrorMessage = false;
  private String nameErrorMessage;
  private boolean showNameErrorMessage = false;

  public int calculateAgeFromDOB() {
    Calendar birth = new GregorianCalendar();
    Calendar today = new GregorianCalendar();
    int calculatedAge = 0;
    int factor = 0;

    Date currentDate = new Date()// current date

    birth.setTime(dob);
    today.setTime(currentDate);

    if (today.get(Calendar.DAY_OF_YEAR< birth.get(Calendar.DAY_OF_YEAR)) {
      factor = -1;

    }
    calculatedAge = today.get(Calendar.YEAR- birth.get(Calendar.YEAR)
        + factor;
    return calculatedAge;
  }

  public void calculateAgeListener(ActionEvent event) {

    int calculatedAge = calculateAgeFromDOB();
    if (event.getComponent().getId().equals("confirm")) {
      if (calculatedAge != this.age) {
        this.isAgeCorrected = true;
        this.output = null;
        this.age = calculatedAge;
      }

    }

  }

  public void valueChangeInput(ValueChangeEvent event) {

    if (event.getComponent().getId().equals("name")) {
      System.out.println("inside name change");
      if (event.getNewValue().equals("Vampire")) {
        showNameErrorMessage = true;
        nameErrorMessage = "The name 'Vampire' is not allowed";
        output = null;
      else {
        this.output = "submitted";
      }
    }
     if(event.getComponent().getId().equals("age")){
       // doing nothing
       System.out.println("inside age change");
    
     }
  }

  public boolean isSubmitted() {
    return submitted;
  }

  public String submitUserDetails() {
    int calculatedAge = calculateAgeFromDOB();
    if (calculatedAge != this.age) {
      this.output = null;
      this.showErrorMessage = true;
      errorMessage = "Click Confirm Age button to correct your age";
    else {
      this.output = "submitted";
    }
    submitted = true;
    return this.output;
  }

  public Date getDob() {
    return dob;
  }

  public void setDob(Date dob) {
    this.dob = dob;
  }

  public String getName() {
    return name;
  }

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

  public Integer getAge() {
    return age;
  }

  public void setAge(Integer age) {
    this.age = age;
  }

  public String getEmail() {
    return email;
  }

  public void setEmail(String email) {
    this.email = email;
  }
  
  public boolean isShowErrorMessage() {
    return showErrorMessage;
  }

  public String getNameErrorMessage() {
    return nameErrorMessage;
  }

  public boolean isShowNameErrorMessage() {
    return showNameErrorMessage;
  }

  public String getErrorMessage() {
    return errorMessage;
  }

  public void setErrorMessage(String errorMessage) {
    this.errorMessage = errorMessage;
  }

  public boolean isAgeCorrected() {
    return isAgeCorrected;
  }

}

Run your webapplication

To run your web application, right click on BasicJSFProject, click- >run as -> run on server.
Select the server type ,Apache Tomacat v6.0 Server.Click Fininsh.

Open a web browser and type the url : http://localhost:8080/BasicJSFProject/

If  you enter 'Vampire' in the name field and click Submit button, your page will look like this:

                                                                     

Now we have seen event handling working in JSF. To know what happens behind the scene, see the article JSF Request Processing Life cycle.

Listener as class

Implementing Listener class to handle an Action Event

Listener class must  implement javax.faces.event.ActionListener interface and must include a method called processAction(ActionEvent ActionEvent) which will hold the actual logic. This method is invoked when the Action Event occurs

public class ActionListenerImpl implements ActionListener{
         public void processAction(ActionEvent event) throws AbortProcessingException {
                if (event.getComponent().getId().equals("confirm")) {
                     System.out.println("Inside processAction ");
                }
       }
}

Registering an Action Listener on a Component

We can register an ActionListener implementation on a UICommand component by nesting an <f:actionListener> tag within the component’s tag. The type attribute of the <f:actionListener> tag specifies the fully qualified class name of the ActionListener implementation.

<h:commandButton id="confirm" value="Confirm Age">
       <f:actionListener type ="com.user.details.ActionListenerImpl"/>
</h:commandButton>


Implementing Listener class to handle a Value-Change Event

Listener class must implement javax.faces.event.ValueChangeListener interface and must include a method called processValueChange(ValueChangeEvent valueChangeEvent) which will hold the actual logic. This method is invoked by the JavaServer Faces implementation when the value-change event occurs. The ValueChangeEvent instance stores the old and the new values of the component that fired the event.

public class ValueChangeListenerImpl implements ValueChangeListener {
       public void processValueChange(ValueChangeEvent event)
       throws AbortProcessingException {
           if (event.getComponent().getId().equals("name")) {
                System.out.println("inside name change");
           }
      }
}

Registering an Value-Change Listener on a Component

We can register an ValueChangeListener implementation on a component by nesting an <f:valueChangeListener> tag within the component’s tag. The type attribute of the <f:valueChangeListener> tag specifies the fully qualified class name of the ValueChangeListener implementation.

<h:inputText id="name" value="#{userDetails.name}" required="true"
      requiredMessage="#{msg.enter_name}"
      validatorMessage="#{msg.enter_name_validlength}" >
      <f:valueChangeListener type = "com.user.details.ValueChangeListenerImpl"/>
      <f:validateLength minimum="3" />
</h:inputText>

With proper implementation of methods, this approch will also work same as 'Listener As Backing Bean Method' implementation.

<< Previous       Next  >>

See More Topics:

How page navigation works in JSF?
How to use resource bundle in JSF?
How to implement Internationalization and Localization in JSF?
JSF Request Processing Life Cycle with example.