Handling Events in JavaServer Faces, Part 2
Triggering an Event by Changing a Value
When the user changes the value of an
input component, a ValueChangeEvent is fired when the form is
submitted. You have similar options for handling a
ValueChangeEvent as you have for an
ActionEvent: use a value change listener method
binding or attach one or more ValueChangeListener
to the component:
<!-- Using a method binding expression -->
<h:selectBooleanCheckbox valueChangeListener="#{myBean.handleNewValue}" />
<!-- Using one or more listeners -->
<h:selectBooleanCheckbox>
<f:valueChangeListener type="com.mycompany.LogEventListener" />
<f:valueChangeListener type="com.mycompany.HandleNewValueListener" />
</h:selectBooleanCheckbox>
Two things make the JSF ValueChangeEvent less
important than similar events in a traditional GUI framework: it
fires only when the form is submitted (because
that's when the server can detect the change), and
JSF's mechanisms for validation and automatic model
updates handle most of the typical use cases. That said,
it's still useful for some features, especially if
you know that your users use JavaScript-enabled browsers and are
connected to the server via a high-speed connection so frequent
requests don't cause performance problems.
In this section, we look at how to use a
ValueChangeEvent triggered by a change in a
checkbox value to switch between the standard and extended report
entry type choices. Example 8-6 shows the entry form
page modified for this purpose.
Example 8-6. Entry form area JSP page with a checkbox for extending the type choices (expense/stage4/entryFormArea.jsp)
<%@ page contentType="text/html" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<f:view>
<h:form>
Title:
<h:inputText id="title" size="30" required="true"
value="#{reportHandler.currentReport.title}" />
<h:message for="title" />
<br>
Entry:
<br>
Date:
<h:inputText id="date" size="8" required="true"
value="#{entryHandler.currentEntry.date}">
<f:convertDateTime dateStyle="short" />
</h:inputText>
<h:message for="date" />
<br>
Type:
<h:selectOneMenu id="type" required="true"
value="#{entryHandler.currentEntry.type}">
<f:selectItems value="#{entryHandler.currentChoices}"/>
</h:selectOneMenu>
Extended Choices:
<h:selectBooleanCheckbox immediate="true" onchange="this.form.submit( );"
valueChangeListener="#{entryHandler.toggleTypes}" />
<h:message for="type" />
<br>
Amount:
<h:inputText id="amount" size="8" required="true"
value="#{entryHandler.currentEntry.amount}">
<f:convertNumber pattern="#,##0.00" />
<f:validateDoubleRange minimum="1"/>
</h:inputText>
<h:message for="amount" />
<br>
<h:commandButton value="Add"
disabled="#{reportHandler.addEntryDisabled}"
action="#{entryHandler.add}" />
</h:form>
<h:messages layout="table" globalOnly="true" />
<%-- Loop to verify that it works --%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<ol>
<c:forEach items="${reportHandler.currentReportEntries}" var="e">
<li>Date: ${e.date}, Type: ${e.type}, Amount: ${e.amount}</li>
</c:forEach>
</ol>
</f:view>
The <h:commandButton> element is replaced
with an <h:selectBooleanCheckbox> element in
Example 8-6. Its immediate
attribute is set to true, causing the
ValueChangeEvent to fire in the Apply Request
Values phase instead of in the Process Validations phase. This means
that we can handle the event without causing any validation error
messages to be displayed.
The onchange attribute contains JavaScript that
submits the form the checkbox belongs to when its value is changed,
so that the user doesn't have to click on any button
for the change to take effect.
Finally, the valueChangeListener attribute binds
the component to a value change listener method in the
entryHandler bean. This method looks almost the
same as the toggleTypes() method used for the
Ext/Std button in Example 8-4, but it has a
different signature:
package com.mycompany.expense;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;
...
public class EntryHandler {
...
public void toggleTypes(ValueChangeEvent event) {
includeSpecial = !includeSpecial;
FacesContext.getCurrentInstance( ).renderResponse( );
}
...
}
A value change listener method is a method with a
ValueChangeEvent as its single parameter and a
void return type. The toggleType(
) method used here doesn't use the event
at all. It just flips the includeSpecial flag, the
same as the method used in the previous section.
What's different is that it also tells JSF to jump
straight to the Render Response phase by calling the
FacesContext renderResponse() method. For an
action method, this happens automatically, but for all other method
binding method types you must do so explicitly when needed. The rest
remains the same as when we used a button to trigger the type list
changes.
The event handling concepts and mechanisms that I've
described in this chapter may take some time to truly understand, but
when you do, they make sense given the nature of a web application
with the client separated from the server. In the next chapter,
we'll add to what you've learned
here, looking at how to tell JSF to switch to a new view depending on
the event-handling outcome.
Hans Bergsten
is the founder of Gefion Software and author of O'Reilly's JavaServer Pages, 3rd Edition.
Return to ONJava.com.