Event handling

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

The event handler then executes some application functionality, in sufficient details, to appropriately react to a particular user action.

The Nexaweb client defines approximately 25 events for the available components. (See the API documentation for details on what events the Nexaweb Client can fire for each component. The event models differ for Ajax and Java.) Nexaweb applications can handle events through:

ResourceDescription
MacroA named group of valid page instructions.
MCOs

Client-side code including calls to system services on the client.

Server side resourcesJSP pages, Servlets, static pages and so forth.

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
  • Passes the event object to the client-side MCO handler or system service
  • Encodes the event information and sends it by HTTP to a server-side handler

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

And then what happens . . . . ?

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).

Creating Event Handlers

You can specify event handlers using a container or a URL.

Containers hold client behavior. URLs are directly specified in the UI document using XModify syntax that request a resource from the server and run it on the client.

There are three types of containers:

  • MCO - holds user defined functionality
  • Macro - holds user defined functionality
  • System containers - holds system objects that have APIs available.

Container syntax appears similar to the following:

<container>:<id or name of object in container>.<methods available>(<parameters>)

A macro call appears similar to the following:

macro:MyMacro.execute(‘hello’)
 

Handling Events on the Client or Server

You can create event handlers to run on the client or the server.

Client-side

If the server does not need to be notified of an event, the event can be handled immediately on the client-side to update the client UI, create or update different documents, or even connect to the server again.

Server-side

Server-Side event handling is most appropriate for long-running transactions, server-side event or messaging centric applications, or even for applications whose business logic changes from 'click to click'. Server-side event handling will increase the load on server resources and thus should be considered carefully during application architecture. ( JSF applications tend to require significantly more server-side resources because most of the event handling is done at the server tier. With an n-tier web architecture, you can offload a significant portion of application processing to the client.)

Client-Side Event Handling

Your application can handle events on the client-side by using a Macro, an Managed Client Object (MCO), or a call to a system service.

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://openxal.org/core/macro" 
  name="changeLabelTextMacro">
  <xm:modifications xmlns:xm="http://openxal.com/core/xmodify">
      <xm:set-attribute select="id(''mylabel'')">
         <xm:attribute name="text" value="Some new text"/>
      </xm:set-attribute>
  </xm:modifications>
</macro:macro>
	
<button onCommand="macro://changeLabelTextMacro.execute()" 
        text="Change mylabel text" />

<label id="mylabel" text="This is some text" />

Invoking Handlers on the Client-Side

The following examples show the 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://openxal.org/core/mco">
  <mco:mco id="eventMco" src="pkg.EventHandlers"/>
</mco:declarations>
<xal>
  <rootPane>
   <flowPane/>
      <button onCommand="mco:eventMco.changeLabelText()" 
              text="Change mylabel text" />
      <label id="mylabel" text="This is some text" />
  </rootPane>
</xal>

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() misses 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;
}

Server-side Event Handling

Server-side handlers can be any type of server-side resource including server-pages (any technology), server-based services (a servlet, listener, or other resource), or even web services calls. Handlers can also be static files with XModify statements.

The following examples show the use of server-side handling.

1. A JSP page receiving event parameters and generating an XModify 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:

<xm:modifications xmlns:xm="http://openxal.com/core/xmodify">
  <xm:set-attribute select="id('mylabel')">
    <xm:attribute name="text" value="Button 
         <%= (request.getParameter("id") + " fired event " + 
         request.getParameter("event")) %>"/>
  </xm:set-attribute>
</xm: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. 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.

2. UI Syntax that can process LOGIN information on either the client or the server, depending on the button that gets pressed.

The UI definition syntax:

<xal xmlns="http://openxal.org/ui">
  <mco xmlns="http://openxal.org/core/mco" id="loginMco" class="LoginMco"/>
  <rootPane>
    <verticalBoxPane align="start" padding="40px" width="300px" x="100px">
      <label height="30px" id="loginAlert" text="" 
       fontSize="16" color="red"/>
      <label height="30px" text="Please enter user name and password:" />
      <horizontalBoxPane xmlns="http://openxal.org/ui/html" 
        height="50px" width="250px">
        <label height="20px" text="Login:" width="100px"/>
        <textField height="25px" text="" width="110px" id="name"/>
      </horizontalBoxPane>

      <horizontalBoxPane xmlns="http://openxal.org/ui/html" 
        height="50px" width="250px">
        <label height="20px" text="Password:" width="100px"/>
        <passwordField height="25px" text="" width="110px" id="pass"/>
      </horizontalBoxPane>

      <horizontalBoxPane xmlns="http://openxal.org/ui/html" 
        height="50px" width="250px">
        <label height="20px" text="Client Side Validation" width="140px"/>
        <button height="25px" text="Submit" width="75px"
          onCommand="mco:loginMco.validateClient(name.text, 
          pass.text, loginAlert)"/>
      </horizontalBoxPane>

      <horizontalBoxPane xmlns="http://openxal.org/ui/html" 
        height="50px" width="250px">
        <label height="20px" text="Serverside Validation" width="140px"/>
        <button height="25px" text="Submit" width="75px"
          onCommand="mco:loginMco.validateServer(name.text, 
          pass.text, loginAlert)"/>
      </horizontalBoxPane>

    </verticalBoxPane>
  </rootPane>
</xal>

3. Client-side code (loginMco ) processes all events, directing where validation occurs: on client or server.

Client Code: loginMco.java

import java.text.MessageFormat;
import com.nexaweb.client.mco.AbstractMco;
import com.nexaweb.client.netservice.NetServiceException;
import com.nexaweb.xml.Element;
	
public class LoginMco extends AbstractMco {
  // process request on the client
    public void validateClient(String name, String pass, Element label) {
      String result = "password".equals(pass) ? "Success! Hello " + 
        name+"!": "Client validation fails! (Hint: password is 'password')";
	label.setAttribute("text", result);
    }

  // send request to the server 
  public void validateServer(String name, String pass, Element label) {

    String result = "Exception!"; 

    try {
      result = new String(getSession().getRequestService().
      retrieve(
        MessageFormat.format("login.jsp?name={0}&pass={1}", 
        new Object [] { name, pass })).getContent());
      } catch (NetServiceException e) {

      }
      label.setAttribute("text", result);
    }
  }
}

Server Code: login.jsp

<%
 String name = request.getParameter("name");

 String pass = request.getParameter("pass");
 if(pass.equals("password")){
   response.getWriter().append("Success! Hello ").
   append(name).append("!");
 }else{ 
   response.getWriter().append("Server validation fails! 
   (Hint: password is 'password')");
 }
%>

Note: You do not have to use a client MCO to manage the request to the server. You can register the login.jsp directly to the onCommand event. If you do this, you will need to access the server session or server side documents. To do this, 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.