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 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.
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;
}
}
}
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>
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
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";
}
}
}
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
<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>
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>
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; |
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 ");
}
}
}
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>
<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");
}
}
}
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>
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.
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.