Download FAQ History |  | API Search Feedback

> ## Handling Events

> As explained in Event and Listener Model, 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:

> - Implement an event listener to handle the event
> - Register the event listener on the component

> When emitting events from custom components, you need to manually queue the event on the `FacesContext`. Handling Events for Custom Components 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 be aware of registering listeners. See Handling Events for Custom Components 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.

Download FAQ History |  | API Search Feedback

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.