Skip Headers
Oracle® Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework (Oracle Fusion Applications Edition)
11g Release 1 (11.1.1.6.2)

Part Number E28163-02
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
PDF · Mobi · ePub

5 Handling Events

This chapter describes how to handle events on the server as well as on the client.

This chapter includes the following sections:

5.1 Introduction to Events and Event Handling

In traditional JSF applications, event handling typically takes place on the server. JSF event handling is based on the JavaBeans event model, where event classes and event listener interfaces are used by the JSF application to handle events generated by components.

Examples of user events in an application include clicking a button or link, selecting an item from a menu or list, and changing a value in an input field. When a user activity occurs such as clicking a button, the component creates an event object that stores information about the event and identifies the component that generated the event. The event is also added to an event queue. At the appropriate time in the JSF lifecycle, JSF tells the component to broadcast the event to the appropriate registered listener, which invokes the listener method that processes the event. The listener method may trigger a change in the user interface, invoke backend application code, or both.

Like standard JSF components, ADF Faces command components deliver ActionEvent events when the components are activated, and ADF Faces input and select components deliver ValueChangeEvent events when the component local values change.

For example, in the File Explorer application, the File Menu contains a submenu whose commandMenuItem components allow a user to create a new file or folder. When users click the Folder commandMenuItem, an ActionEvent is invoked. Because the EL expression set as the value for the component's actionListener attribute resolves to the createNewDirectory method on the headerManager managed bean, that method is invoked and a new directory is created.

Note:

Any ADF Faces component that has built-in event functionality must be enclosed in the form tag.

While ADF Faces adheres to standard JSF event handling techniques, it also enhances event handling in two key ways by providing:

5.1.1 Events and Partial Page Rendering

Unlike standard JSF events, ADF Faces events support AJAX-style partial postbacks to enable partial page rendering (PPR). Instead of full page rendering, ADF Faces events and components can trigger partial page rendering, that is, only portions of a page refresh upon request.

Certain components are considered event root components. Event root components determine boundaries on the page, and so allow the lifecycle to run just on components within that boundary (for more information about this aspect of the lifecycle, see Section 4.3, "Using the Optimized Lifecycle"). When an event occurs within an event root, only those components that are children to the root are refreshed on the page. An example of an event root component is a popup. When an event happens within a popup, only the popup and its children are rerendered, and not the whole page.

Additionally, certain events indicate a specific component as an event root component. For example, the disclosure event sent when a expanding or collapsing a showDetail component (see Section 8.8, "Displaying and Hiding Contents Dynamically"), indicates that the showDetail component is a root. The lifecycle is run only on the showDetail component (and any child components or other components that point to this as a trigger), and only they are rerendered when it is expanded or collapsed.

Table 5-1 shows the event types in ADF Faces, and whether or not the source component is an event root.

Table 5-1 Events and Event Root Components

Event Type Component Trigger Is Event Root

action

All command components

false

dialog

dialog

false

disclosure

showDetail, showDetailHeader

true

disclosure

showDetailItem

true

focus

tree, treeTable

true

launch

All command components

NA

launchPopup

inputListOfValues, inputComboboxListOfValues

true

load

document

NA

poll

poll

true

popupOpened

popup

NA

popupOpening

popup

NA

popupClosed

popup

NA

propertyChange

All components

NA

queryEvent

query, quickQuery

true

queryOperation

query, quickQuery

true

rangeChange

table

NA

regionNavigation

region

NA

return

All command components

true

returnPopupData

inputListOfValues, inputComboboxListOfValues

true

returnPopup

inputListOfValues, inputComboboxListOfValues

true

rowDisclosure

tree, treeTable

true

selection

tree, treeTable, table

true

sort

treeTable, table

true

valueChange

All input and select components (components that implement EditableValueHolder)

true


Tip:

If components outside of the event root need to be processed when the event root is processed, then you must set the partialTrigger attribute on those components to the ID of the event root component.

5.1.2 Client-Side Event Model

In addition to server-side action and value change events, ADF Faces components also invoke client-side action and value change events, and other kinds of server and client events. Some events are generated by both server and client components (for example, selection events); some events are generated by server components only (for example, launch events); and some events are generated by client components only (for example, load events).

By default, most client events are propagated to the server. Changes to the component state are automatically synchronized back to the server to ensure consistency of state, and events are delivered, when necessary, to the server for further processing. However, you can configure your event so that it does not propagate.

In addition, any time you register a client-side event listener on the server-side Java component, the RCF assumes that you require a JavaScript component, so a client-side component is created.

Client-side JavaScript events can come from several sources: they can be derived automatically from DOM events, from property change events, or they can be manually created during the processing of other events.

5.2 Using ADF Faces Server Events

ADF Faces provides a number of server-side events. Table 5-2 lists the events generated by ADF Faces components on the server, and the components that trigger them.

Table 5-2 ADF Faces Server Events

Event Triggered by Component...

ActionEvent

All command components

DialogEvent

dialog

DisclosureEvent

showDetail, showDetailHeader, showDetailItem

FocusEvent *

tree, treeTable

LaunchEvent

All command components

LaunchPopupEvent

inputListOfValues, inputComboboxListOfValues

LoadEvent **

document

PollEvent

poll

QueryEvent

query, quickQuery

QueryOperationEvent

query, quickQuery

RangeChangeEvent

table

RegionNavigationEvent

region

ReturnEvent

All command components

ReturnPopupEvent

inputListOfValues, inputComboboxListOfValues

RowDisclosureEvent

tree, treeTable

SelectionEvent

tree, treeTable, table

SortEvent

treeTable, table

ValueChangeEvent

All input and select components (components that implement EditableValueHolder)


* This focus event is generated when focusing in on a specific subtree, which is not the same as a client-side keyboard focus event.

** The LoadEvent event is fired after the initial page is displayed (data streaming results may arrive later).

All server events have event listeners on the associated component(s). You need to create a handler that processes the event and then associate that handler code with the listener on the component.

For example, in the File Explorer application, a selection event is fired when a user selects a row in the table. Because the table's selectionListener attribute is bound to the tableSelectFileItem handler method on the TableContentView.java managed bean, that method is invoked in response to the event.

To handle server-side events:

  1. In a managed bean (or the backing bean for the page that will use the event listener), create a public method that accepts the event (as the event type) as the only parameter and returns void. Example 5-1 shows the code for the tableSelectFileItem handler. (For information about creating and using managed beans, see Section 2.6, "Creating and Using Managed Beans.")

    Example 5-1 Event Listener Method

    public void tableSelectFileItem(SelectionEvent selectionEvent)
      {
        FileItem data = (FileItem)this.getContentTable().getSelectedRowData();
        setSelectedFileItem(data);
      }
    

    Tip:

    If the event listener code is likely to be used by more than one page in your application, consider creating an event listener implementation class that all pages can access. All server event listener class implementations must override a processEvent() method, where Event is the event type.

    For example, the LaunchListener event listener accepts an instance of LaunchEvent as the single argument. In an implementation, you must override the event processing method, as shown in the following method signature:

    public void processLaunch (LaunchEvent evt) 
    {
      // your code here
    }
    
  2. To register an event listener method on a component, in the Structure window, select the component that will invoke the event. In the Property Inspector, use the dropdown menu next to the event listener property, and choose Edit.

  3. Use the Edit Property dialog to select the managed bean and method created in Step 1.

    Example 5-2 shows sample code for registering a selection event listener method on a table component.

    Example 5-2 Registering an Event Listener Method

    <af:table id="folderTable" var="file"
    . . . 
              rowSelection="single"
              selectionListener="#{explorer.tableContentView.tableSelectFileItem}"
    . . .
    </af:table>
    

5.3 Using JavaScript for ADF Faces Client Events

Most components can also work with client-side events. Handling events on the client saves a roundtrip to the server. When you use client-side events, instead of having managed beans contain the event handler code, you use JavaScript, which can be contained either on the calling page or in a JavaScript library.

By default, client events are processed only on the client. However, some event types are also delivered to the server, for example, AdfActionEvent events, which indicate a button has been clicked. Other events may be delivered to the server depending on the component state. For example, AdfValueChangeEvent events will be delivered to the server when the autoSubmit attribute is set to true. You can cancel an event from being delivered to the server if no additional processing is needed. However, some client events cannot be canceled. For example, because the popupOpened event type is delivered after the popup window has opened, this event delivery to the server cannot be canceled.

Performance Tip:

If no server processing is needed for an event, consider canceling the event at the end of processing so that the event does not propagate to the server. For more information, see Section 5.3.5, "How to Prevent Events from Propagating to the Server."

Table 5-3 lists the events generated by ADF Faces client components, whether or not events are sent to the sever, whether or not the events are cancelable, and the components that trigger the events.

Table 5-3 ADF Faces Client Events

Event Type Event Class Propagates to Server Can Be Canceled Triggered by Component

action

AdfActionEvent

Yes

Yes

All command components

dialog

AdfDialogEvent

Yes

Yes

dialog

When user selects the OK or Cancel button in a dialog

disclosure

AdfDisclosureEvent

Yes

Yes

showDetail, showDetailHeader, showDetailItem

When the disclosure state is toggled by the user

 

AdfFocusEvent

Yes

Yes

tree, treeTable

 

AdfLaunchPopupEvent

Yes

Yes

inputListOfValues, inputComboboxListOfValues

inlineFrameLoad

AdfDomComponentEvent

Yes

Yes

inlineFrame

When the internal iframe fires its load event.

load

AdfComponentEvent

Yes

Yes

document

After the document's contents have been displayed on the client, even when PPR navigation is used. It does not always correspond to the onLoad DOM event.

 

AdfPollEvent

Yes

Yes

poll

popupOpened

AdfPopupOpenedEvent

No

No

popup

After a popup window or dialog is opened

popupOpening

AdfPopupOpeningEvent

No

Yes

popup

Prior to opening a popup window or dialog

popupClosed

AdfPopupClosedEvent

No

No

popup

After a popup window or dialog is closed

propertyChange

AdfPropertyChangeEvent

No

No

All components

query

AdfQueryEvent

Yes

Yes

query, quickQuery

Upon a query action (that is, when the user clicks the search icon or search button)

queryOperation

AdfQueryOperationEvent

Yes

Yes

query, quickQuery

 

AdfReturnEvent

Yes

Yes

All command components

 

AdfReturnPopupDataEvent

Yes

Yes

inputListOfValues, inputComboboxListOfValues

 

AdfReturnPopupEvent

Yes

Yes

inputListOfValues, inputComboboxListOfValues

rowDisclosure

AdfRowDisclosureEvent

Yes

Yes

tree, treeTable

When the row disclosure state is toggled

selection

AdfSelectionEvent

Yes

Yes

tree, treeTable, table

When the selection state changes

sort

AdfSortEvent

Yes

Yes

treeTable, table

When the user sorts the table data

touchStart
touchMove
touchEnd
touchCancel

AdfComponentTouchEvent

No

Yes

All

valueChange

AdfValueChangeEvent

Yes

Yes

All input and select components (components that implement EditableValueHolder)

When the value of an input or select component is changed


ADF Faces also supports client keyboard and mouse events, as shown in Table 5-4

Table 5-4 Keyboard and Mouse Event Types Supported

Event Type Event Fires When...

click

User clicks a component

dblclick

User double-clicks a component

mousedown

User moves mouse down on a component

mouseup

User moves mouse up on a component

mousemove

User moves mouse while over a component

mouseover

Mouse enters a component

mouseout

Mouse leaves a component

keydown

User presses key down while focused on a component

keyup

User releases key while focused on a component

keypress

When a successful keypress occurs while focused on a component

focus

Component gains keyboard focus

blur

Component loses keyboard focus


Best Practice:

Keyboard and mouse events wrap native DOM events using the AdfUIInputEvent subclass of the AdfBaseEvent class, which provides access to the original DOM event and also offers a range of convenience functions for retrieval of key codes, mouse coordinates, and so on. The AdfBaseEvent class also accounts for browser differences in how these events are implemented. Consequently, you must avoid invoking the getNativeEvent() method on the directly, and instead use the AdfUIInputEvent API.

The clientListener tag provides a declarative way to register a client-side event handler script on a component. The script will be invoked when a supported client event type is fired. Example 5-3 shows an example of a JavaScript function associated with an action event.

Example 5-3 clientListener Tag

<af:commandButton id="button0"
                  text="Do something in response to an action">
  <af:clientListener method="someJSMethod" type="action"/>
</af:commandButton>

Tip:

Use the clientListener tag instead of the component's JavaScript event properties.

5.3.1 How to Use Client-Side Events

To use client-side events, you need to first create the JavaScript that will handle the event. You then use a clientListener tag.

To use client-side events:

  1. Create the JavaScript event handler function. For information about creating JavaScript, see Section 3.3, "Adding JavaScript to a Page." Within that functionality, you can add the following:

    • Locate a client component on a page

      If you want your event handler to operate on another component, you must locate that component on the page. For example, in the File Explorer application, when users choose the Give Feedback menu item in the Help menu, the associated JavaScript function has to locate the help popup dialog in order to open it. For more information about locating client components, see Section 3.5, "Locating a Client Component on a Page."

    • Return the original source of the event

      If you have more than one of the same component on the page, your JavaScript function may need to determine which component issued the event. For example, say more than one component can open the same popup dialog, and you want that dialog aligned with the component that called it. You must know the source of the AdfLaunchPopupEvent in order to determine where to align the popup dialog. For more information, see Section 5.3.2, "How to Return the Original Source of the Event."

    • Add client attributes

      It may be that your client event handler will need to work with certain attributes of a component. For example, in the File Explorer application, when users choose the About menu item in the Help menu, a dialog launches that allows users to provide feedback. The function used to open and display this dialog is also used by other dialogs, which may need to be displayed differently. Therefore, the function needs to know which dialog to display along with information about how to align the dialog. This information is carried in client attributes. Client attributes can also be used to marshall custom server-side attributes to the client. For more information, see Section 5.3.3, "How to Use Client-Side Attributes for an Event."

    • Cancel propagation to the server

      Some of the components propagate client-side events to the server, as shown in Table 5-3. If you do not need this extra processing, then you can cancel that propagation. For more information, see Section 5.3.5, "How to Prevent Events from Propagating to the Server."

  2. Once you create the JavaScript function, you must add an event listener that will call the event method.

    Note:

    Alternatively, you can use a JSF 2.0 client behavior tag (such as f:ajax) to respond to the client event, as all client events on ADF Faces components are also exposed as client behaviors. For more information, see the Java EE 6 tutorial (http://download.oracle.com/javaee/index.html)

    1. Select the component to invoke the JavaScript, and in the Property Inspector, set ClientComponent to true.

    2. In the Component Palette, from the Operations panel, drag a Client Listener and drop it as a child to the selected component.

    3. In the Insert Client Listener dialog, enter the method and select the type for the JavaScript function.

      The method attribute of the clientListener tag specifies the JavaScript function to call when the corresponding event is fired. The JavaScript function must take a single parameter, which is the event object.

      The type attribute of the clientListener tag specifies the client event type that the tag will listen for, such as action or valueChange. Table 5-3 lists the ADF Faces client events.

      The type attribute of the clientListener tag also supports client event types related to keyboard and mouse events. Table 5-4 lists the keyboard and mouse event types.

      Example 5-4 shows the code used to invoke the showHelpFileExplorerPopup function from the Explorer.js JavaScript file.

      Example 5-4 clientListener Tags on JSF Page

      <af:commandMenuItem id="feedbackMenuItem"
                          text="#{explorerBundle['menuitem.feedback']}"
                          clientComponent="true">
        <af:clientListener method="Explorer.showHelpFileExplorerPopup"
                           type="action"/>
      </af:commandMenuItem>
      
    4. Add any attributes required by the function by dragging a Client Attribute from the Operations panel of the Component Palette, and dropping it as a child to the selected component. Enter the name and value for the attribute in the Property Inspector. Example 5-5 shows the code used to set attribute values for the showAboutFileExplorerPopup function.

      Example 5-5 Adding Attributes

      <af:commandMenuItem id="aboutMenuItem"
                           text="#{explorerBundle['menuitem.about']}"
                           clientComponent="true">
          <af:clientListener method="Explorer.showAboutFileExplorerPopup"
                              type="action"/>
          <af:clientAttribute name="popupCompId" value=":fe:aboutPopup"/>
          <af:clientAttribute name="align" value="end_after"/>
          <af:clientAttribute name="alignId" value="aboutMenuItem"/>
        </af:commandMenuItem>
      

      Note:

      If you use the attribute tag (instead of the clientAttribute tag) to add application-specific attributes or bonus attributes to a server component, those attributes are not included on the client component equivalent. You can use the clientAttribute tag on the JSF page, and the value will then be available on both the server and client. For information about posting client values back to the server, see Section 5.4, "Sending Custom Events from the Client to the Server." For information about bonus attributes, see Section 3.8, "Using Bonus Attributes for Client-Side Components."

5.3.2 How to Return the Original Source of the Event

The JavaScript method getSource() returns the original source of a client event. For example, the File Explorer application contains the showAboutFileExplorerPopup function shown in Example 5-6, that could be used by multiple events to set the alignment on a given popup dialog or window, using client attributes to pass in the values. Because each event that uses the function may have different values for the attributes, the function must know which source fired the event so that it can access the corresponding attribute values (for more about using client attributes, see Section 5.3.3, "How to Use Client-Side Attributes for an Event").

Example 5-6 Finding the Source Component of a Client Event

Explorer.showAboutFileExplorerPopup = function(event)
{
  var source = event.getSource();
  var alignType = source.getProperty("align");
  var alignCompId = source.getProperty("alignId"); 
  var popupCompId = source.getProperty("popupCompId");
  
  source.show({align:alignType, alignId:alignCompId});
  
  event.cancel();
}

The getSource() method is called to determine the client component that fired the current focus event, which in this case is the popup component.

5.3.3 How to Use Client-Side Attributes for an Event

There may be cases when you want the script logic to cause some sort of change on a component. To do this, you may need attribute values passed in by the event. For example, the File Explorer application contains the showAboutFileExplorerPopup function shown in Example 5-7, that can be used to set the alignment on a given popup component, using client attributes to pass in the values. The attribute values are accessed by calling the getProperty method on the source component.

Example 5-7 Attribute Values Are Accessed from JavaScript

Explorer.showAboutFileExplorerPopup = function(event)
{
  var source = event.getSource();
  var alignType = source.getProperty("align");
  var alignCompId = source.getProperty("alignId"); 
  var popupCompId = source.getProperty("popupCompId");
  
  var aboutPopup = event.getSource().findComponent(popupCompId);  
  aboutPopup.show({align:alignType, alignId:alignCompId});
  
  event.cancel();
}

The values are set on the source component, as shown in Example 5-8.

Example 5-8 Setting Attributes on a Component

<af:commandMenuItem id="aboutMenuItem"
                    text="#{explorerBundle['menuitem.about']}"
                    clientComponent="true">
  <af:clientListener method="Explorer.showAboutFileExplorerPopup"
                     type="action"/>
  <af:clientAttribute name="popupCompId" value=":aboutPopup"/>
  <af:clientAttribute name="align" value="end_after"/>
  <af:clientAttribute name="alignId" value="aboutMenuItem"/>
</af:commandMenuItem>

Using attributes in this way allows you to reuse the script across different components, as long as they all trigger the same event.

5.3.4 How to Block UI Input During Event Execution

There may be times when you do not want the user to be able to interact with the UI while a long-running event is processing. For example, suppose your application uses a button to submit an order, and part of the processing includes creating a charge to the user's account. If the user were to inadvertently press the button twice, the account would be charged twice. By blocking user interaction until server processing is complete, you ensure no erroneous client activity can take place.

The ADF Faces JavaScript API includes the AdfBaseEvent.preventUserInput function. To prevent all user input while the event is processing, you can call the preventUserInput function, and a glass pane will cover the entire browser window, preventing further input until the event has completed a roundtrip to the server.

You can use the preventUserInput function only with custom events, events raised in a custom client script, or events raised in a custom client component's peer. Additionally, the event must propagate to the server. Example 5-9 shows how you can use preventUserInput in your JavaScript.

Example 5-9 Blocking UI Input

function queueEvent(event)
{
   event.cancel(); // cancel action event
   var source = event.getSource();
         
   var params = {};
   var type = "customListener";
   var immediate = true;
   var isPartial = true;
   var customEvent =  new AdfCustomEvent(source, type, params, immediate);
   customEvent.preventUserInput();
   customEvent.queue(isPartial);
}   

5.3.5 How to Prevent Events from Propagating to the Server

By default, some client events propagate to the server once processing has completed on the client. In some circumstances, it is desirable to block this propagation. For instance, if you are using a commandButton component to execute JavaScript code when the button is clicked, and there is no actionListener event listener on the server, propagation of the event is a waste of resources. To block propagation to the server, you call the cancel() function on the event in your listener. Once the cancel() function has been called, the isCanceled() function will return true.

Example 5-10 shows the showAboutFileExplorerPopup function, which cancels its propagation.

Example 5-10 Canceling a Client Event from Propagating to the Server

Explorer.showAboutFileExplorerPopup = function(event)
{
  var source = event.getSource();
  var alignType = source.getProperty("align");
  var alignCompId = source.getProperty("alignId"); 
  var popupCompId = source.getProperty("popupCompId");
  
  var aboutPopup = event.getSource().findComponent(popupCompId);  
  aboutPopup.show({align:alignType, alignId:alignCompId});
  
  event.cancel();
}

Canceling an event may also block some default processing. For example, canceling an AdfUIInputEvent event for a context menu will block the browser from showing a context menu in response to that event.

The cancel() function call will be ignored if the event cannot be canceled, which an event indicates by returning false from the isCancelable() function (events that cannot be canceled show "no" in the Is Cancelable column in Table 5-3). This generally means that the event is a notification that an outcome has already completed, and cannot be blocked. There is also no way to uncancel an event once it has been canceled.

5.3.6 What Happens at Runtime: How Client-Side Events Work

Event processing in general is taken from the browser's native event loop. The page receives all DOM events that bubble up to the document, and hands them to the peer associated with that piece of DOM. The peer is responsible for creating a rich client JavaScript event object that wraps that DOM event, returning it to the page, which queues the event (for more information about peers and the ADF Faces architecture, see Chapter 3, "Using ADF Faces Architecture").

The event queue on the page most commonly empties at the end of the browser's event loop once each DOM event has been processed by the page (typically, resulting in a component event being queued). However, because it is possible for events to be queued independently of any user input (for example, poll components firing their poll event when a timer is invoked), queueing an event also starts a timer that will force the event queue to empty even if no user input occurs.

The event queue is a First-In-First-Out queue. For the event queue to empty, the page takes each event object and delivers it to a broadcast() function on the event source. This loop continues until the queue is empty. It is completely legitimate (and common) for broadcasting an event to indirectly lead to queueing a new, derived event. That derived event will be broadcast in the same loop.

When an event is broadcast to a component, the component does the following:

  1. Delivers the event to the peer's DispatchComponentEvent method.

  2. Delivers the event to any listeners registered for that event type.

  3. Checks if the event should be bubbled, and if so initiates bubbling. Most events do bubble. Exceptions include property change events (which are not queued, and do not participate in this process at all) and, for efficiency, mouse move events.

    While an event is bubbling, it is delivered to the AdfUIComponent HandleBubbledEvent function, which offers up the event to the peer's DispatchComponentEvent function. Note that client event listeners do not receive the event, only the peers do.

    Event bubbling can be blocked by calling an event's stopBubbling() function, after which the isBubblingStopped() function will return true, and bubbling will not continue. As with cancelling, you cannot undo this call.

    Note:

    Canceling an event does not stop bubbling. If you want to both cancel an event and stop it from bubbling, you must call both functions.

  4. If none of the prior work has canceled the event, calls the AdfUIComponent.HandleEvent method, which adds the event to the server event queue, if the event requests it.

5.3.7 What You May Need to Know About Using Naming Containers

Several components in ADF Faces are NamingContainer components, such as pageTemplate, subform, table, and tree. When working with client-side API and events in pages that contain NamingContainer components, you should use the findComponent() method on the source component.

For example, because all components in any page within the File Explorer application eventually reside inside a pageTemplate component, any JavaScript function must use the getSource() and findComponent() methods, as shown in Example 5-11. The getSource() method accesses the AdfUIComponent class, which can then be used to find the component.

Example 5-11 JavaScript Using the findComponent() Method

function showPopup(event)
{
  event.cancel();
  var source = event.getSource();
  var popup = source.findComponent("popup");
  popup.show({align:"after_end", alignId:"button"});
}

When you use the findComponent() method, the search starts locally at the component where the method is invoked. For more information about working with naming containers, see Section 3.5, "Locating a Client Component on a Page."

5.4 Sending Custom Events from the Client to the Server

While the clientAttribute tag supports sending bonus attributes from the server to the client, those attributes are not synchronized back to the server. To send any custom data back to the server, use a custom event sent through the AdfCustomEvent class and the serverListener tag.

The AdfCustomEvent.queue() JavaScript method enables you to fire a custom event from any component whose clientComponent attribute is set to true. The custom event object contains information about the client event source and a map of parameters to include on the event. The custom event can be set for immediate delivery (that is, during the Apply Request Values phase), or non-immediate delivery (that is, during the Invoke Application phase).

For example, in the File Explorer application, after entering a file name in the search field on the left, users can press the Enter key to invoke the search. As Example 5-12 shows, this happens because the inputText field contains a clientListener that invokes a JavaScript function when the Enter key is pressed.

Example 5-12 clientListener Invokes JavaScript Function and Causes ServerLIstener to Be Invoked

//Code on the JSF page...
<af:inputText id="searchCriteriaName"
              value="#{explorer.navigatorManager.searchNavigator.
                                               searchCriteriaName}"
              shortDesc="#{explorerBundle['navigator.filenamesearch']}">
  <af:serverListener type="enterPressedOnSearch"
                     method="#{explorer.navigatorManager.
                                        searchNavigator.searchOnEnter}"/>
  <af:clientListener type="keyPress"
                     method="Explorer.searchNameHandleKeyPress"/>
</af:inputText>

//Code in JavaScript file...
Explorer.searchNameHandleKeyPress = function (event)
{
  if (event.getKeyCode()==AdfKeyStroke.ENTER_KEY)
  {
    var source = event.getSource();
    AdfCustomEvent.queue(source,
                         "enterPressedOnSearch", 
                         {}, 
                         false);
  } 
}

The JavaScript contains the AdfCustomEvent.queue method that takes the event source, the string enterPressedOnSearch as the custom event type, a null parameter map, and False for the immediate parameter.

The inputText component on the page also contains the following serverListener tag:

<af:serverListener type="enterPressedOnSearch"
                   method="#{explorer.navigatorManager.
                                      searchNavigator.searchOnEnter}"/>

Because the type value enterPressedOnSearch is the same as the value of the parameter in the AdfCustomEvent.queue method in the JavaScript, the method that resolves to the method expression #{explorer.navigatorManager.searchNavigator.searchOnEnter} will be invoked.

5.4.1 How to Send Custom Events from the Client to the Server

To send a custom event from the client to the server, fire the client event using a custom event type, write the server listener method on a backing bean, and have this method process the custom event. Next, register the server listener with the component.

To send custom events:

  1. Create the JavaScript that will handle the custom event using the AdfCustomEvent.queue() method to provide the event source, custom event type, and the parameters to send to the server.

    For example, the JavaScript used to cause the pressing of the Enter key to invoke the search functionality uses the AdfCustomEvent.queue method that takes the event source, the string enterPressedOnSearch as the custom event type, a null parameter map, and False for the immediate parameter, as shown in Example 5-13.

    Example 5-13 Sample JavaScript for Custom Events

    Explorer.searchNameHandleKeyPress = function (event)
    {
      if (event.getKeyCode()==AdfKeyStroke.ENTER_KEY)
      {
        var source = event.getSource();
        AdfCustomEvent.queue(source,
                             "enterPressedOnSearch", 
                             {}, 
                             false);
      } 
    }
    
  2. Create the server listener method on a managed bean. This method must be public and take an oracle.adf.view.rich.render.ClientEvent object and return a void type. Example 5-14 shows the code used in the SearchNavigatorView managed bean that simply calls another method to execute the search and then refreshes the navigator.

    Example 5-14 Server Listener Method for a Custom Client Event

    public void searchOnEnter(ClientEvent clientEvent)
      {
        doRealSearchForFileItem();
        
        // refresh search navigator
        this.refresh();
      }
    

    Note:

    The Java-to-JavaScript transformation can lose type information for Numbers, chars, Java Objects, arrays, and nonstring CharSequences. Therefore, if an object being sent to the server was initially on the server, you may want to add logic to ensure the correct conversion. See Section 5.4.3, "What You May Need to Know About Marshalling and Unmarshalling Data."

  3. Register the clientListener by dragging a Client Listener from the Operations panel of the Component Palette, and dropping it as a child to the component that raises the event.

    Note:

    On the component that will fire the custom client event, the clientComponent attribute must be set to true to ensure that a client-side generated component is available.

  4. In the Insert Client Listener dialog, enter the method and type for the JavaScript function. Be sure to include a library name if the script is not included on the page. The type can be any string used to identify the custom event, for example, enterPressedOnSearch was used in the File Explorer.

  5. Register the server listener by dragging a Server Listener from the Operations panel of the Component Palette, and dropping it as a sibling to the clientListener tag.

  6. In the Insert Server Listener dialog, enter the string used as the Type value for the client listener, as the value for this server listener, for example enterPressedOnSearch.

    In the Property Inspector, for the method attribute, enter an expression that resolves to the method created in Step 2.

5.4.2 What Happens at Runtime: How Client and Server Listeners Work Together

At runtime, when the user initiates the event, for example, pressing the Enter key, the client listener script executes. This script calls the AdfCustomEvent.queue() method, and a custom event of the specified event type is queued on the input component. The server listener registered on the input component receives the custom event, and the associated bean method executes.

5.4.3 What You May Need to Know About Marshalling and Unmarshalling Data

Marshalling and unmarshalling is the process of converting data objects of a programming language into a byte stream and back into data objects that are native to the same or a different programming language. In ADF Faces, marshalling and unmarshalling refer to transformation of data into a suitable format so that it can be optimally exchanged between JavaScript on the client end and Java on the server end. When the client is browser-based, the two common strategies for marshalling are JavaScript Object Notation (JSON) and XML. ADF Faces uses a mix of both of these strategies, with the information sent from the server to the client mostly as JSON and information sent from the client to the server as XML (for more information about JSON, see http://www.json.org).

When you send information from JavaScript to Java, the JavaScript data objects are converted (marshalled) into XML, which is then parsed back or unmarshalled into Java objects at the server-side. For example, consider a JSF page that has a commandButton component whose ID is cmd. When a user clicks the commandButton component, the client must communicate to the server that an actionEvent has been fired by this specific commandButton. In the requestParameter map, the information is mapped with the key using the format event + . + id where id is the ID of the component. So the requestParameter map key for the commandComponent would be the XML string stored as the value of the key event.cmd.

The XML fragment after marshalling in this example would be:

<m xmlns="http:/oracle.com/richClient/comm"><k v="type"><s>action</s></k></m>

The m in the example means that this should be unmarshalled into a map. The k denotes the key and the value is of type String. On the server side, this XML fragment is parsed into a java.util.Map of one entry having type (java.lang.String) as the key and action (java.lang.String) as the value.

The unmarshalled information is grouped per client ID, stored in the request map, and used when the components are being decoded. So in this example, when the commandButton is decoded, it will check for the presence of any client events using its client ID (event.cmd) and then queue an action event if one is found (the decode behavior is implemented in the renderer hierarchy for commandButton component).

Table 5-5 shows the mapping between corresponding JavaScript and Java types.

Table 5-5 JavaScript to Java Type Map

JavaScript Type Java Type

Boolean

java.lang.Boolean

Number

java.lang.Double

String

java.lang.String

Date

java.util.Date

Array

java.util.ArrayList

Object

java.util.Map


Marshalling from Java to JavaScript happens mostly through JSON. This type of marshalling is straightforward as JSON is the object literal notation in JavaScript. The client-components usually have their properties encoded in JSON. Consider the following example:

new AdfRichCommandButton('demoTemplate:richComand'
    {'partialSubmit':true,'useWindow':false})

The second argument ({'partialSubmit':true,'useWindow':false}) is a JSON object. There is no additional unmarshalling step required at the browser end as JSON can directly be parsed into the JavaScript environment as an object.

Encoding for a table also uses JSON to pass push messages to the client. The following is an example of an envelope containing a single encoded push message:

[{'rKey':'0','type':'update','data':[{'val':'Active Data Every Second: on row 0:78','prop':'value','cInd':0},{'val':'Active Data Every Second: on row 0:78','prop':'value','cInd':1}]}]

The envelope is a JavaScript Array with only one object, which describes the message. This message contains information about the type of change, the actual value of the data, and so on, that is then used by the client-side table peer to update the table itself.

Table 5-6 shows the mapping between corresponding Java and JavaScript types.

Table 5-6 Java to JavaScript Type Map

Java Type JavaScript Type

java.lang.Boolean

Boolean

java.lang.Double

Number

java.lang.Integer

Number

java.lang.Float

Number

java.lang.Long

Number

java.lang.Short

Number

java.lang.Character

String

java.lang.CharSequence

String

java.util.Collection

Array

java.util.Date

Date

java.util.Map

Object

Array

Array

java.awt.Color

TrColor


Note that there could be some loss of information during the conversion process. For example, say you are using the following custom event to send the number 1 and the String test, as shown in the following example:

AdfCustomEvent.queue(event.getSource(), "something", {first:1, second:"test"});

In the server-side listener, the type of the first parameter would become a java.lang.Double because numbers are converted to Doubles when going from JavaScript to Java. However, it might be that the parameter started on the server side as an int, and was converted to a number when conversion from Java to JavaScript took place. Now on its return trip to the server, it will be converted to a Double.

5.5 Executing a Script Within an Event Response

Using the ExtendedRenderKitService class, you can add JavaScript to an event response, for example, after invoking an action method binding. It can be a simple message like sending an alert informing the user that the database connection could not be established, or a call to a function like hide() on a popup window to programatically dismiss a popup dialog.

For example, in the File Explorer application, when the user clicks the UpOneFolder navigation button to move up in the folder structure, the folder pane is repainted to display the parent folder as selected. The HandleUpOneFolder() method is called in response to clicking the UpOneFolder button event. It uses the ExtendedRenderKitService class to add JavaScript to the response.

Example 5-15 shows the UpOneFolder code in the page with the actionListener attribute bound to the HandleUpOneFolder() handler method which will process the action event when the button is clicked.

Example 5-15 Invoking a Method to Add JavaScript to a Response

<af:commandToolbarButton id="upOneFolder"
. . .
        actionListener="#{explorer.headerManager.handleUpOneFolder}"/>

Example 5-16 shows the handleUpOneFolder method that uses the ExtendedRenderKitService class.

Example 5-16 Adding JavaScript to a Response

public void handleUpOneFolder(ActionEvent actionEvent)
 {
   UIXTree folderTree = 
     feBean.getNavigatorManager().getFoldersNavigator().getFoldersTreeComponent();
   Object selectedPath = 
    feBean.getNavigatorManager().getFoldersNavigator().getFirstSelectedTreePath();
    
   if (selectedPath != null)
    {
      TreeModel model = 
        _feBean.getNavigatorManager().getFoldersNavigator().getFoldersTreeModel();
      Object oldRowKey = model.getRowKey();
      try
      {
        model.setRowKey(selectedPath);
        Object parentRowKey = model.getContainerRowKey();
        if (parentRowKey != null)
        {
          folderTree.getSelectedRowKeys().clear();
          folderTree.getSelectedRowKeys().add(parentRowKey);
          // This is an example of how to force a single attribute
          // to rerender. The method assumes that the client has an optimized
          // setter for "selectedRowKeys" of tree.
          FacesContext context =  FacesContext.getCurrentInstance();
          ExtendedRenderKitService erks =
            Service.getRenderKitService(context,
                    ExtendedRenderKitService.class);
          String clientRowKey = folderTree.getClientRowKeyManager().
            getClientRowKey(context, folderTree, parentRowKey);
          String clientId = folderTree.getClientId(context);
          StringBuilder builder = new StringBuilder();
          builder.append("AdfPage.PAGE.findComponent('");
          builder.append(clientId);
          builder.append("').setSelectedRowKeys({'");
          builder.append(clientRowKey);
          builder.append("':true});");
          erks.addScript(context, builder.toString());
        }
      }
      finally
      {
        model.setRowKey(oldRowKey);
      }
      // Only really needed if using server-side rerendering
      // of the tree selection, but performing it here saves
      // a roundtrip (just one, to fetch the table data, instead
      // of one to process the selection event only after which
      // the table data gets fetched!)
      _feBean.getNavigatorManager().getFoldersNavigator().openSelectedFolder();
    } 
    
  }

5.6 Using Client Behavior Tags

ADF Faces client behavior tags provide declarative solutions to common client operations that you would otherwise have to write yourself using JavaScript, and register on components as client listeners. By using these tags instead of writing your own JavaScript code to implement the same operations, you reduce the amount of JavaScript code that needs to be downloaded to the browser.

ADF Faces provides these client behavior tags that you can use in place of client listeners:

Client behavior tags cancel server-side event delivery automatically. Therefore, any actionListener or action attributes on the parent component will be ignored. This cannot be disabled. If you want to also trigger server-side functionality, you should use either a client-side event (see Section 5.3, "Using JavaScript for ADF Faces Client Events"), or add an additional client listener that uses AdfCustomEvent and af:serverListener to deliver a server-side event (see Section 5.4, "Sending Custom Events from the Client to the Server").

5.6.1 How to Use the scrollComponentIntoViewBehavior Tag

Use the scrollComponentIntoViewBehavior tag when you want the user to be able to jump to a particular component on a page. This action is similar to an anchor in HTML. For example, you may want to allow users to jump to a particular part of a page using a commandLink component. For the richTextEditor and inlineFrame components, you can jump to a subcomponent. For example, Figure 5-1 shows a richTextEditor component with a number of sections in its text. The command links below the editor allow the user to jump to specific parts of the text.

Figure 5-1 scrollComponentIntoViewBehavior Tag in an Editor

scrollComponentIntoViewBehavior Tag in an Editor

You can also configure the tag to have focus switched to the component to which the user has scrolled.

To use the scrollComponentIntoViewBehavior tag:

  1. Create a command component that the user will click to jump to the named component. For procedures, see Section 18.2.1, "How to Use Command Buttons and Command Links."

  2. In the Component Palette, from the Operations section, drag and drop a Scroll Component Into View Behavior as a child to the command component.

  3. In the Insert Scroll Component Into View Behavior dialog, use the dropdown arrow to select Edit and then navigate to select the component to which the user should jump.

  4. In the Property Inspector, set the focus attribute to true if you want the component to have focus after the jump.

  5. For a richTextEditor or inlineFrame component, optionally enter a value for the subTargetId attribute. This ID is defined in the value of the richTextEditor or inlineFrame component.

    For example, the value of the subTargetId attribute for the scrollComponentIntoViewBehavior tag shown in Figure 5-1 is Introduction. The value of the richTextEditor is bound to the property shown in Example 5-17. Note that Introduction is the ID for the first header.

    Example 5-17 subTargetId Value Defined in a Property

    private static final String _RICH_SECTIONED_VALUE = 
      "<div>\n" + 
      "    <h2>\n" + 
      "      <a id=\"Introduction\"></a>Introduction</h2>\n" + 
      "    <p>\n" + 
      "      The ADF Table component is used to display a list of structured data. For example,\n" + 
      "      if we have a data structure called Person that has two properties - firstname and\n" + 
      "      lastname, we could use a Table with two columns - one for firstname, and the other\n" + 
      "      for lastname - to display a list of Person objects.\n" + 
      "    </p>\n" + 
      "  </div>\n" + 
      "  <div>\n" + 
      "    <h2>\n" + 
      "      <a id=\"The_Table_Model\"></a>The Table Model</h2>\n" + 
      "    <p>\n" + 
      . . .
       </div>";
    

5.7 Using Polling Events to Update Pages

ADF Faces provides the poll component whose pollEvent can be used to communicate with the server at specified intervals. For example, you might use the poll component to update an outputText component, or to deliver a heartbeat to the server to prevent a user from being timed out of their session.

You need to create a listener for the pollEvent that will be used to do the processing required at poll time. For example, if you want to use the poll component to update the value of an outputText component, you would implement a pollEventListener method that would check the value in the data source and then update the component.

You can configure the interval time to determine how often the poll component will deliver its poll event. You also configure the amount of time after which the page will be allowed to time out. This can be useful, as the polling on a page causes the session to never time out. Each time a request is sent to the server, a session time out value is written to the page to determine when to cause a session time out. Because the poll component will continually send a request to the server (based on the interval time), the session will never time out. This is expensive both in network usage and in memory.

To avoid this issue, the web.xml configuration file contains the oracle.adf.view.rich.poll.TIMEOUT context-parameter, which specifies how long a page should run before it times out. A page is considered eligible to time out if there is no keyboard or mouse activity. The default timeout period is set at ten minutes. So if user is inactive for 10 minutes, that is, does not use the keyboard or mouse, then the framework stops polling, and from that point on, the page participates in the standard server-side session timeout (for more information, see Section A.2.3.19, "Session Timeout Warning").

If the application does time out, when the user moves the mouse or uses the keyboard again, a new session timeout value is written to the page, and polling starts again.

You can override this time for a specific page using the poll component's timeout attribute.

5.7.1 How to Use the Poll Component

When you use the poll component, you normally also create a handler method to handle the functionality for the polling event.

Before You Begin

It may be helpful to have an understanding of how the attributes can affect functionality. For more information, see Section 5.7, "Using Polling Events to Update Pages"

To use a poll component:

  1. In a managed bean, create a handler for the poll event. For more information about managed beans, see Section 2.6, "Creating and Using Managed Beans"

  2. Create a poll component by dragging and dropping a Poll from the Operations panel of the Component Palette.

  3. In the Property Inspector, expand the Common section and set the following:

    • Interval: Enter the amount of time in milliseconds between poll events. Set to 0 to disable polling.

    • PollListener: Enter an EL expression that evaluates to the method in Step 1.

  4. If you want to override the global timeout value in the web.xml file, expand the Other section and set Timeout to the amount of time in milliseconds after which the page will stop polling and the session will time out.