NXML Event Handling

Nexaweb provides the ability to act on events at levels varying from direct hardware responses such as onMouseOver to logical events such as onStateChange. Nexaweb-enabled applications can handle these through:

  • NXML macro commands
  • Server side resources such as JSP pages, Servlets and static pages
  • Client-side java code including calls to MCOs and system services

 This section covers the following topics:

  • Event overview
  • Using macros to handle events
  • Server-side event handling
  • Client-side event handling
  • Event ordering

Event Overview

In event-driven programming, you define an event handler for each event to which the application needs to react.  Such events include:

  • Physical events such as a keystroke, button click, or mouse hovering over a component
  • Higher-level UI events such as a selection being made from a dropdown list or a checkbox changing state.

Event handlers for these events might include a:

  • Macro - a client-side script included in the XML defining the UI
  • Server-side document
  • Method of a user-defined client class or MCO

In any case, the event handler executes some application functionality, in sufficient details, to appropriately react to a particular user action. The Nexaweb client relies on events to respond to user commands.The Nexaweb client defines approximately 30 events.  See the UI documentation for details on what events the Nexaweb client can fire for each component.

All events cause the client to create an event object with information describing the event.  After the client creates the event object, the client either:

  • Executes the macro
  • Encodes the event information and sends it by HTTP to a server-side handler
  • Passes the event object to the client-side MCO hand

For example, given the following code:

<button text="my button" onCommand="buttonclick.jsp" />

The onCommand event fires to buttonclick.jsp on the server, and automatically passes the following as URL parameters:

  • id attribute - a user defined id or a Nexaweb assigned id
  • event attribute - the 'onCommand' in this case

 Using Macro Event Handlers

You can define a client-side macro script in the UI document to handle a client event. The following example defines:

This macro causes the client to update the label's text to "Some new text," when a user clicks the button.

<macro:macro xmlns:macro="http://nexaweb.com/macro" 
    name="changeLabelTextMacro">
    <xu:modifications document="nxml" version="1.0" 
      xmlns:xu="http://nexaweb.com/xupdate">
      <xu:set-attribute select="id(''mylabel'')">
        <xu:attribute name="text" value="Some new text"/>
      </xu:set-attribute>
    </xu:modifications>
</macro:macro>

<button onCommand="macro://changeLabelTextMacro.execute()" 
        text="Change mylabel text" />
<label id="mylabel" text="This is some text" />

Server-Side Event Handling

Firing a client event to the server is a more powerful tool when the server is interested in the user's action on the client side. The handler can be a JSP page, a servlet, or a static page with XUpdate statements.  

The following example shows a JSP page receiving event parameters and generating an XUpdate document to return to the client.

The UI document specifies the handler:

<button onCommand="changeLabelText.jsp" text="Change mylabel text" />
<label id="mylabel" text="This is some text" />

changeLabelText.jsp processes the event on the server side:

<xu:modifications document="nxml" version="1.0" 
  xmlns:xu="http://nexaweb.com/xupdate">
  <xu:set-attribute select="id('mylabel')">
    <xu:attribute name="text" value="Button <%= 
       (request.getParameter("id") + " fired event " + 
        request.getParameter("event")) %>"/>
  </xu:set-attribute>
</xu:modifications>

Here, in the server side jsp page, you use request.getParameter("id") and request.getParameter("event") to get the button element's id and the name of the event it fires.  You do not need to specify the id or event value when you set the onCommand attribute; these values are attached to the URL at runtime.  Again, check the UI reference documentation for the events that can be fired from different UI elements.

When handling a client event on the server side, if you need to access the server session or server side documents, you can call:

SessionManager smgr = ServiceManager.getSessionManager(); ServerSession nxsession = smgr.findByRequest(request);

If the server UI DOM is enabled, you can get the UI document on the server side with:

 Document uiDom = nxsession.getDocumentRegistry().getUiDocument();

and walk the returned Document or find an element by name using the API in com.nexaweb.xml.

Client-Side Event Handling

If the server does not need to be notified of an event, the event can be handled immediately by a call to a system service or to a customer-created client-side class called a Managed Client Object (MCO).  This client-side method can update the client UI, create or update different documents, or even connect to the server again.  The following examples show uses of client-side code invocation for handling events. 

1. Simple Element Access and Attribute Change

This example shows code that gets the clientSession and then the label element. It then calls the ClientSession's getEventHandler().getClientEvent() to get the ClientEvent object.  This object provides the event's parameters.  Available parameters vary by event types.

In this example, the UI document declares the MCO and specifies the handler:

 <mco:declarations xmlns:mco="http://nexaweb.com/mco">
   <mco:mco id="eventMco" src="pkg.EventHandlers"/>
 </mco:declarations>
<nxml>
  <rootPane>
    <flowLayout/>
    <button onCommand="mco://eventMco.changeLabelText()" 
            text="Change mylabel text" />
    <label id="mylabel" text="This is some text" />
  </rootPane>
</nxml>

The handler is method changeLabelText in user-created EventHandlers.java:

 private static final XPath LABEL_XPATH = 
  XPathFactory.createXPath("id('mylabel')");
    
 public void changeLabelText(){
   ClientSession s = getSession();
   Element label = LABEL_XPATH.evaluateAsElement(
   s.getDocumentRegistry().getUiDocument());
        
   Object domLock =
    label.getOwnerDocument().getDomSynchronizationObject(); 
        
   ClientEvent event = s.getEventHandler().getClientEvent();
   synchronized(domLock){
     label.setAttribute("text", "some new text from "
     + event.getParameter("id") + ":" + event.getParameter("event"));
   }
 }

2. Performing a Time-Consuming Client-Side Task

When performing a time consuming task in an event handler, you can notify the user to wait through a wait cursor and then perform your task in a separate thread. The following example shows how to do this. This example shows a task that takes 5 seconds to complete before updating the label. To let the user know the client is working on the task, it does displayService.pushGlassPane() to show a wait cursor and prevent the user from entering input.  At the end of the task, displayService.popGlassPane() dismisses the wait cursor and allows input again.

The following shows the UI document:

<button onCommand="mco://eventMco.waitOnLongTask()" text="Change mylabel text" />
<label id="mylabel" text="This is some text" />

The following shows the MCO handler method:

 public void waitOnLongTask(){
   final ClientSession s = getSession();
   s.getDisplayService().pushGlassPane();
   new Thread(new Runnable(){
     public void run(){
       try{
         Thread.sleep(5000);
         onChangeLabelText();
       }catch(Exception e){
         e.printStackTrace();
       }finally{
         s.getDisplayService().popGlassPane();       
       }
     }
   }).start();
  }
}

3. An onBeforeActiveLost Event Handler

This is a special event that you can use when you want to add validation code to an element or check other conditions before the focus is lost from the current element. The client fires the onBeforeActiveLost event handler before it fires an element's onActiveLost. When the event handler returns false, the event is consumed; in other words, the focus stays in the currently focused element.  Only a a client-side MCO can handle an onBeforeActiveLost event handler since neither macros nor server-side handlers can return boolean.  Remember that an onBeforeActiveLost handler must execute very quickly.

The following example ensures that a textField's input length is exactly 10 characters. It prevents the user from being able to click or tab away from the textField if it has fewer or more than 10 characters. 

The following shows the UI document:

 <textField text="input 10 characters only" onBeforeActiveLost="mco://eventMco.onBeforeActiveLost()"/>
 The following shows the MCO handler method:
 public boolean onBeforeActiveLost(){
   ClientSession s = McoContainer.getSession();
   ClientEvent event = s.getEventHandler().getClientEvent();
   String id = event.getParameter("id");
   Element element = 
      s.getDocumentRegistry().getUiDocument().getElementById(id);
   String text = (String)element.getAttribute("text");
   if ( text.length() == 10)
     return true;
   else
     return false;
   }
 }

Event Ordering

The Nexaweb Platform provides many different events, some fired in sequence such as onCommand and onMouseUp for a button.  The following provides general information about event sequences:

Note: Take care when handling multiple events that can fire in sequence. For example, have the first handler set a flag or redirect the second handler.

  • onBeforeActiveLost event fires on a component when it loses focus. If the client returns false to the onBeforeActiveLost event then the component does not lose focus and no other events fire.
  • When a user presses the <Enter> key,  the onCommand event always fires first.
  • For events directly resulting from physical events, the following order of events occurs:
    1. The onKeyDown (in the case of a keyboard action other than <Enter>) or onMouseDown (in the case of a mouse click) events fire first.
    2. Then If there is a change of focus, preceded by an onEdit event in the case where a text component with changed text has lost focus.
    3. Next, onActiveLost fires, followed by onActiveGained.
    4. Higher-level synthesized events such as onStateChange follow the change in focus.
    5. Next, for keyboard events, onKeyChar fires.
    6. On release, onKeyUp fires for the keyboard or onMouseUp for the mouse (preceded by a button's onCommand).