Download
FAQ
History
PrevHomeNext API
Search
Feedback
Divider

Handling Events

As explained in Event and Listener Model (page 804), the JavaServer Faces event and listener model is similar to the JavaBeans event model in that it has strongly typed event classes and listener interfaces. JavaServer Faces technology supports two different kinds of component events: action events and value-changed events.

Action events occur when the user activates a component represented by UICommand. These components include buttons and hyperlinks. These events are represented by the javax.faces.event.ActionEvent class. An implementation of the javax.faces.event.ActionListener handles action events.

Value-changed events result in a change to the local value of a component represented by UIInput or one of its subclasses. One example of a value-changed event is that generated by entering a value in a text field. These events are represented by the javax.faces.event.ValueChangedEvent class. An implementation of the javax.faces.event.ValueChangedListener handles value-changed events.

Both action events and value-changed events can be processed at any stage during the request processing lifecycle. Both ActionListener and ValueChangedListener extend from the common FacesListener interface.

To cause your application to react to action events or value-changed events emitted by a standard component, you need to:

When emitting events from custom components, you need to manually queue the event on the FacesContext. Handling Events for Custom Components (page 927) explains how to do this. The UIInput and UICommand components automatically queue events on the FacesContext.

The rest of this section explains how to implement a ValueChangedListener and an ActionListener and how to register the listeners on components.

Implementing an Event Listener

For each kind of event generated by components in your application, you need to implement a corresponding listener interface. Listeners that handle the action events in an application must implement javax.faces.event.ActionListener. Similarly, listeners that handle the value-changed events must implement javax.faces.event.ValueChangedListener. The cardemo application includes implementations of both of these listeners.


Note: You should not create an ActionListener to handle an event that results in navigating to a page. You should write an Action class to handle events associated with navigation. SeeNavigating Between Pages for more information. ActionListeners should only be used to handle UI changes, such as tree expansion.


By virtue of extending from FacesListener, both listener implementations must implement the getPhaseId method. This method returns an identifier from javax.event.PhaseId that refers to a phase in the request processing lifecycle. The listener must not process the event until after this phase has passed. For example, a listener implementation that updates a component's model object value in response to a value-changed event should return a PhaseId of PhaseId.PROCESS_VALIDATIONS so that the local values pass validation checks before the model object is updated. The phases during which events can be handled are Apply Request Events, Process Validations, and Update Model Values. If your listener implementation returns a PhaseID of PhaseId.ANY_PHASE then the listener will process events during the Apply Request Values phase if possible.

Implementing a Value-Changed Listener

In addition to the getPhaseId method, a ValueChangedListener implementation must include a processValueChanged(ValueChangedEvent) method.

The processValueChanged(ValueChangedEvent) method processes the specified ValueChangedEvent and is invoked by the JavaServer Faces implementation when the ValueChangedEvent occurs. The ValueChangedEvent instance stores the old and the new values of the component that fired the event.

The cardemo application has a new feature that updates the price of the chosen car after an extra option is selected for the car. When the user selects an option, a ValueChangedEvent is generated, and the processValueChanged method of the PackageValueChanged listener implementation is invoked. Here is the processValueChanged method from PackageValueChanged:

public void processValueChanged(ValueChangedEvent vEvent) {
  try {
    String componentId =
      vEvent.getComponent().getComponentId();
    FacesContext context = FacesContext.getCurrentInstance();
    String currentPrice;
    int cPrice = 0;
    currentPrice =
      (String)context.getModelValue(
        "CurrentOptionServer.carCurrentPrice");
    cPrice = Integer.parseInt(currentPrice);
    if ((componentId.equals("currentEngine")) ||
      (componentId.equals("currentBrake")) ||
      (componentId.equals("currentSuspension")) ||
      (componentId.equals("currentSpeaker")) ||
      (componentId.equals("currentAudio")) ||
      (componentId.equals("currentTransmission"))) {
        cPrice = cPrice -
          (this.getPriceFor((String)vEvent.getOldValue()));
        cPrice = cPrice + 
          (this.getPriceFor((String)vEvent.getNewValue()));
    } else {
      Boolean optionSet = (Boolean)vEvent.getNewValue();
      cPrice = 
        calculatePrice(componentId, optionSet, cPrice);
    }
    currentPrice = Integer.toString(cPrice);
    context.setModelValue(
      "CurrentOptionServer.carCurrentPrice", currentPrice);
  } catch (NumberFormatException ignored) {}
} 

This method first gets the ID of the component that fired the event from ValueChangeEvent. Next, it gets the current price of the car from the CurrentOptionServer bean.

The if statement checks if the component that fired the event is one of the SelectItems components. If it is, it subtracts the old value of the selected option from the current price and adds the new value of the selected option to the current price. The getPriceFor(String) method returns the price of an option.

If the component that fired the event is a SelectBoolean, the new value is retrieved from the event. The calculatePrice(String, Boolean, int) method checks if the value is true. If it is, the price returned from getPriceFor(String) for the selected option is added to the current price; otherwise it is subtracted from the current price.

Finally the method updates the current price in the CurrentOptionServer bean.

Implementing Action Listeners

In addition to the getPhaseId method, a ActionListener implementation must include a processAction(ActionEvent) method.

The processAction(ActionEvent) processes the specified ActionEvent and is invoked by the JavaServer Faces implementation when the ActionEvent occurs. The ActionEvent instance stores the value of commandName, which identifies the command or action that should be executed when the component associated with the commandName is activated.

The cardemo application has another new feature that allows a user to select a package, which contains a set of options for their chosen car. These packages are called Custom, Deluxe, Performance, and Standard.

The user selects a package by clicking on one of the buttons representing a package. When the user clicks one of the buttons, an ActionEvent is generated, and the processAction(ActionEvent) method of the CarActionListener listener implementation is invoked. Here is a piece of the processAction(ActionEvent) method from CarActionListener:

public void processAction(ActionEvent event) {
  String actionCommand = event.getActionCommand();
  ResourceBundle rb =
    ResourceBundle.getBundle("cardemo/Resources",
    (FacesContext.getCurrentInstance().getLocale()));
  if (actionCommand.equals("custom")) {
    processCustom(event, rb);
  } else if (actionCommand.equals("standard")) {
    processStandard(event, rb);
  ...
  } else if (actionCommand.equals("recalculate")) {
    FacesContext context = FacesContext.getCurrentInstance();
    String currentPackage = 
      (String)context.getModelValue(
      CurrentOptionServer.currentPackage");
    if (currentPackage.equals("custom")) {
      processCustom(event, rb);
    } else if (currentPackage.equals("standard")) {
      processStandard(event, rb);
    }
    ...
  }else if (actionCommand.equals("buy")) {
    FacesContext context = FacesContext.getCurrentInstance();
    context.setModelValue("CurrentOptionServer.packagePrice",
    context.getModelValue(
      "CurrentOptionServer.carCurrentPrice"));
  }
} 

This method gets the commandName from the specified ActionEvent. Each of the UICommand components on more.jsp has its own unique commandName, but more than one component is allowed to use the same commandName. If one of the package buttons is clicked, this method calls another method to process the event according to the specified commandName. For example, processStandard(ActionEvent, ResourceBundle) sets each component's model value in CurrentOptionServer according to the options included in the Standard package. Since the engine options allowed in the Standard package are only V4 and V6, the processStandard(ActionEvent, ResourceBundle) method sets the engineOption property to an array containing V4 and V6.

If the Recalculate button is clicked, this method gets the value of currentPackage from the CurrentOptionServer bean. This value corresponds to the commandName associated with one of the package buttons. The method then calls the appropriate method to process the event associated with the current package.

If the Buy button is clicked, this method updates the packagePrice property of CurrentOptionServer with the current price.

Registering Listeners on Components

A page author can register a listener implementation on a component by nesting either a valuechanged_listener tag or an action_listener tag within the component's tag on the page.

Custom components and renderers also have the option of registering listeners themselves, rather than requiring the page author to register listeners. See Handling Events for Custom Components (page 927) for more information.

This section explains how to register the PackageValueChanged listener and the CarActionListener implementations on components.

Registering a ValueChangedListener on a Component

A page author can register a ValueChangedListener on a UIInput component or a component that extends from UIInput by nesting a valuechanged_listener tag within the component's tag on the page. Several components on the more.jsp page have the PackageValueChanged listener registered on them. One of these components is currentEngine:

<h:selectone_menu  id="currentEngine"
  valueRef="CurrentOptionServer.currentEngineOption">
  <f:valuechanged_listener 
    type="cardemo.PackageValueChanged" />
  <h:selectitems 
    valueRef="CurrentOptionServer.engineOption"/>
</h:selectone_menu>     

The type attribute of the valuechanged_listener tag specifies the fully-qualified class name of the ValueChangedListener implementation.

After this component tag is processed and local values have been validated, the component instance represented by this tag will automatically queue the ValueChangeEvent associated with the specified ValueChangedListener to the FacesContext. This listener processes the event after the phase specified by the getPhaseID method of the listener implementation.

Registering an ActionListener on a Component

A page author can register an ActionListener on a UICommand component by nesting an action_listener tag within the component's tag on the page. Several components on the more.jsp page have the CarActionListener listener implementation registered on them, as shown by the custom tag:

<h:command_button id="custom" commandName="custom"
  commandClass="package-selected"
  key="Custom" bundle="carDemoBundle">
  <f:action_listener type="cardemo.CarActionListener" />
</h:command_button> 

The component tag must specify a commandName that specifies what action should be performed when the component is activated. The ActionEvent is constructed with the component ID and the commandName. More than one component in a component tree can have the same commandName if the same command is executed for those components.

The type attribute of the action_listener tag specifies the fully-qualified class name of the ActionListener implementation.

When the component associated with this tag is activated, the component's decode method (or its associated Renderer) automatically queues the ActionEvent associated with the specified ActionListener to the FacesContext. This listener processes the event after the phase specified by the getPhaseID method of the listener implementation.

Divider
Download
FAQ
History
PrevHomeNext API
Search
Feedback
Divider

All of the material in The Java(TM) Web Services Tutorial is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.