Dispatching Events from an ActionScript Class

A little while ago I was working on a project that required data from an external source.  In trying to follow good development practices, I divided the interface code for the external source into a separate ActionScript class.  No problems there, but the external source may not respond to a request immediately.   This means that my ActionScript class is asynchronous, and will need to tell the calling object when the data is ready.

I’ve used Flex long enough to know that when I see anything asynchronous, I will be dealing with the event model.   “No problem.”, says I,  “I’ve used events in MXML components before.  Add a meta-data tag, dispatch the event using dispatchEvent, and add a listener in the calling object.  Bob’s your uncle, its done.  I can even create a small custom event to be really fancy.  The syntax is all in the help files.

Alas, it was not so easy.   When using MXML components, the dispatchEvent function is a property of the mx.core.UIComponent object, but pure ActionScript classes aren’t derived from the UIComponent and therefore don’t have access to the dispatchEvent function.  So how do I dispatch the event form an ActionScript class?

It took me quite a while to find the answer to this simple question.  If you search the internet, there are tons of examples of using the event dispatcher in MXML components.  There are not that many when it comes to using pure ActionScript classes.  The problem isn’t that its difficult to do, its just hard to find how to do it amongst the thousands of results returned in a search.

So in the interest of spreading the knowledge, here is how to dispatch a custom event from a pure ActionScript class.

Source code can be found here.

 

The parts list:

The solution consists for three parts:

  • The ActionScript class.  This is a pure ActionScript object (no MXML) that will perform an asynchronous task.  In this case it will make an external call to get data.
  • The custom event.  This is a custom build event object that will be thrown by the ActionScript class when the asynchronous task is complete.
  • The calling object (aka parent).  This is the MXML, class, etc.  that will use the ActionScript class to get data.

The order of operations is quite simple as well:

  1. The parent object will add an event listener to watch for the custom event
  2. The parent object will then call a function in the ActionScript class
  3. The ActionScript class’ function will perform an asynchronous task.   This may require the addition of its own event listener to know when the task is complete.
  4. The ActionScript class will dispatch a custom event that includes data from the asynchronous task.
  5. The parent object will get the data from the custom event and do something with it.

Here’s a simple diagram that shows what happens:

eventModel

 

The code:

To keep from having to jump around, lets start looking from the bottom up:

The Custom Event

I could have easily used the existing Event object, but a custom event allows me a bit more freedom.  I can create my own event types as well as having my own data objects passed back in the event.

  • The class is fairly simple, it extends the flash.events.Event object
  • There are three types of event that can be returned:
    • EXTERNALDATA_DATAREADY – everything is good and the data is ready to go
    • EXTERNALDATA_NODATAFOUND – there is no data found
    • EXTERNALDATA_ERROR – something went wrong
  • There is an attribute that holds the data called externalData, which uses the ActionScript convention around getters and setters.
  • The constructor has been modified to include the event type and the data itself.

Here is the source code:

package events

{

import flash.events.Event;

public class ExternalDataEvent extends Event {

public static const EXTERNALDATA_DATAREADY:String = ‘ExternalDataReady’;

public static const EXTERNALDATA_NODATAFOUND:String = ‘ExternalDataNoDataFound’;

public static const EXTERNALDATA_ERROR:String = ‘ExternalDataError’;

private var _externalData:XML;

public function ExternalDataEvent(type:String, exData:XML, bubbles:Boolean=false, cancelable:Boolean=false) {

super(type, bubbles, cancelable);

this.externalData = exData;

}

public function get externalData():XML {

return _externalData;

}

public function set externalData(value:XML):void {

_externalData = value;

}

}

}

The ActionScript Class

The ActionScript class serves as the model for this project.  It is called by the parent object, performs an asynchronous call, and returns the result in an ExternalDataEvent event.

In this case I’m just making a simple HTTP request to load an external XML file, but this could just as easily been an SQL call to a database, a web service request, a LiveCycle Data Services call or any other asynchronous request.

  • The class extends flash.events.EventDispatcher. This is very important.   Because the ActionScript class is not an MXML object, it does not have access to the mx.core.UIComponent object.  By extending the EventDispatcher class we can now dispatch our custom event using the dispatchEvent function.
  • The class has one public method readData
  • Since the data is loaded from an HTTPService (an asynchronous event), we need a listener for the returned data.  This is done by the loadData function.
  • The loadData function fires our custom event.  Which event depends on the data.  During the call the type and data are set into the event itself
  • There is also an error function, fault, as a basic error trap.

Here is the source code:

package model

{

import events.ExternalDataEvent;

import flash.events.EventDispatcher;

import flash.events.Event;

import mx.rpc.events.FaultEvent;

import mx.rpc.events.ResultEvent;

import mx.rpc.http.mxml.HTTPService;

public class ExternalData extends EventDispatcher {

private var HttpService:HTTPService = new HTTPService();

private var xmlURL:String = new String();

public function readData(dataUrl:String):void{

this.load(dataUrl);

}

private function load(dataUrl:String):void{

xmlURL = dataUrl;

HttpService.resultFormat = “e4x”;

HttpService.addEventListener(ResultEvent.RESULT,loadData);

HttpService.addEventListener(FaultEvent.FAULT,fault);

HttpService.url = xmlURL;

HttpService.send();

}

private function loadData(event:ResultEvent):void{

if (event.result != null){

dispatchEvent (new ExternalDataEvent (ExternalDataEvent.EXTERNALDATA_DATAREADY,event.result as XML));

}else {

dispatchEvent (new ExternalDataEvent (ExternalDataEvent.EXTERNALDATA_NODATAFOUND,null));

}

}

private function fault(event:FaultEvent):void{

trace (“ERROR: Problem loading XML data. Stack Trace follows:”);

trace(event.message);

dispatchEvent (new ExternalDataEvent (ExternalDataEvent.EXTERNALDATA_ERROR,null));

}

}

}

 

The Parent Object

Finally we have the parent object.  In this case I built a simple MXML application that will call the ActionScript class and get the returned data from the custom event.

  • The application consists of a button and a text area.  When the button is clicked a click handler function is fired and the retrieved data is displayed in the text area.
  • The click handler adds three listeners.  One for each type of event that can come from the custom event object.
  • The click handler also sets some data and calls the public method in our ActionScript class.
  • The event listeners will be triggered by the ActionScript when it dispatches the custom event (with data).
  • The event listeners set the text of the text area field

Here is the source code:

<?xml version=”1.0″ encoding=”utf-8″?>

<s:Application xmlns:fx=”http://ns.adobe.com/mxml/2009

xmlns:s=”library://ns.adobe.com/flex/spark

xmlns:mx=”library://ns.adobe.com/flex/mx” minWidth=”955” minHeight=”600” xmlns:model=”model.*>

<fx:Declarations>

<model:ExternalData id=”externalData/>

</fx:Declarations>

<fx:Script>

<![CDATA[

import events.ExternalDataEvent;

import model.ExternalData;

protected function getDataBtn_clickHandler(event:MouseEvent):void {

externalData.addEventListener(ExternalDataEvent.EXTERNALDATA_DATAREADY,dataReady);

externalData.addEventListener(ExternalDataEvent.EXTERNALDATA_NODATAFOUND,noDataFound);

externalData.addEventListener(ExternalDataEvent.EXTERNALDATA_ERROR,fault);

var dataURL:String = “\\assets\\testData.xml”;

externalData.readData(dataURL);

}

private function dataReady(event:ExternalDataEvent):void{

dataResults.text = event.externalData.toXMLString();

}

private function noDataFound(event:ExternalDataEvent):void{

dataResults.text = “No Data Found”;

}

private function fault(event:ExternalDataEvent):void{

dataResults.text = “Data Fault”;

}

]]>

</fx:Script>

<s:Button id=”getDataBtn” x=”31” y=”40” label=”Get Data” click=”getDataBtn_clickHandler(event)”/>

<s:TextArea id=”dataResults” x=”88” y=”86/>

</s:Application>

Results

When the user clicks on the Get Data button the ActionScript class is called.  Data is loaded and the custom event is dispatched.  The parent application is watching for the custom event and it populates the text area with the data from that event.

If all goes well, here’s what you should see once you click on the button:

image

 

Like I said earlier, its not terribly difficult.  At least its no more difficult than doing custom events using MXML components.   There just doesn’t seem to be too many sample using pure ActionScript classes rather than visual based components.

Source code can be found here.