Creating and Handling Events AS 3.0

07.12.08 | Author: Danny

ActionScript 2.0 uses broadcasting functionality to handle its events.  In ActionScript 3.0 we get an upgrade in functionality to a truly event-driven model.  The new event model gives us a more structured and ultimately a more powerful way to handle our events.

New Functions

DispatchEvent:DispatchEvent is a function that is available in the common classes used in ActionScript 3.0.  Any DisplayObject already has the function included.  DispatchEvent is analogous to BroadcastMessage in ActionScript 2.0, although I feel it is actually an upgrade.  DispatchEvent takes an Event object as its parameter.  This allows more refined control of how the event will behave.  You can define if it will dispatch up one level, or if it will bubble up until it is caught.  You can also define if you want the event to be cancelable.  You then dispatch events as follows:

DispatchEvent(new Event("myEventName"));

It is a good practice to use a public static class variable for your event name.  We will discuss why that is useful later on.  A dispatch would look like this:

DispatchEvent(new Event(EVENTNAME));//assume EVENTNAME is a public static string.

AddEventListener:  AddEventListener is analogous to addListener in ActionScript 2.0, but the syntax is more usable.  You no longer have to define function names that will match the broadcast signal you are listening for.  It is also more granular because you define every event it will pay attention too.

A simple example will demonstrate the difference. Say we have a class in ActionScript 2.0 that broadcasts three messages: "onCreate", "onClick", "onDelete". So you could have code as follows:

//function inside our class in ActionScript 2.0
myNav.addListener(this);
myHeader.addListener(this);
//bracket to end this function
}

private function onCreate()
{ }

private function onClick()
{ }

private function onDelete()
{ }

If something is listening to that class, it will potentially react to all three of those events.  The control of what it does or does not listen too, is implicit by the presence or lack of the function that matches the event name.  This means that you have to reserve those function names in that object for the events.

Another potential hang up is that you could very easily find other child objects in that class that will dispatch an onCreate event.  For example, if the header and nav both broadcastMessage("onCreate"), then you would run the exact same function.  This means that the re-usability of classes you create can be hampered by potential overlap in event names.

With ActionScript 3.0, adding an event listener also requires that you specify which event you are listening for, and which function to fire.  So you could do the following code:



     //assume we are in a class function for clean code practices
     myNav.addEventListener("onCreate",onNavCreated);
     myNav.addEventListener("onClick",onNavClicked);
     myNav.addEventListener("onDelete",onNavDeleted);
     myHeader.addEventListener("onCreate",onHeaderCreated);
     //finish off this function
}
private function onNavCreated(e:Event):void
{ }
private function onNavClicked(e:Event):void
{ }
private function onNavDeleted(e:Event):void
{ }
private function onHeaderCreated(e:Event):void
{}

 

The ActionScript 3.0 code only listens to events you explicitly tell it to listen for.  If you were to omit one of those addEventListener calls, you do not respond to the event when it is dispatched.  Immediately, you should recognize that this now mitigates any problems with overlapping event names.  Even if the header and the nav both dispatch the same name, it will resolve to the correct function.  You can have your event functions be named anything you need.  It is no longer coupled to the event name. 

It is common practice to define a public static string for all event names a given class can dispatch.  Then you will listen as follows:


myNav.addEventListener(myNav.NAVCREATE,onNavCreated);
myHeader.addEventListener(myHeader.HEADERCREATE,onHeaderCreated);

 

In that example, the actual value in those variables could be exactly the same, but it doesn't matter.  Using the variables provides some clarity to the event model and also ensures that you can ensure your events are correctly matched on the dispatch and listening ends.  You can take advantage of the more strict compiler for ActionScript 3.0 to ensure that your events will always match.  If you type the variable wrong, it won't compile.  Common practice is to have these static variables written in all caps. 

RemoveEventListener: This function is used to stop listening to a specific event.  It has the exact same signature as addEventListener.  You must define the event, and the function when it is removed.

Extending Events

This is a substantial improvement for design patterns and practices.  The new methods for handling events are now in line with methods used in other languages and other environments.  The new methods also present structured interfaces that you can extend and customize to create your own events.  The result is a system that is virtually unlimited in the flexibility it provides you as a developer. 

For example, say you have a class that does asynchronous data calls, and provides the results when they are ready.  You can extend the Event class to create your own Event that includes the added information.  Doing it this way, allows you to set a clearly defined methodology that you can use throughout your system for passing that information.   

Example

//custom event class
package classes.dataUtils
{
    import flash.events.Event;
   public class RemotingEvent extends Event
    {
        public var results:Array;
        public var success:Boolean;
        public function RemotingEvent(t:String,r:Array,s:Boolean)
        {
            super(t);
            results = r;
            success=e;            
        }
        public override function clone():Event
        {
            return new RemotingEvent(type,serverData);
        }
    }
}

//in a different file we could have the following code to do a remoting call

public function doDataCall(sParam:String):void
       {
            var responder:Responder = new Responder(onLookUpResult,onLookUpError);
            try
            {
                netCon.connect(connection_str);
                netCon.call("Project.Class.Function",responder,sParam);
            }
            catch(e:Error)
            {
                netCon.close();
                dispatcher.dispatchEvent(new RemotingEvent(MYCALL_ERROR,null,false));
                trace("error:"+e.message);
            }
            finally
            {
                responder=null;
            }
        }
        private function onLookUpResult(re:Object):void
        {
           netCon.close();
            try
            {
/*Process your return results here.  This is not part of this tutorial. Suffice it to say we create an array from the returned information in the re variable.*/
                 var rEv:RemotingEvent = new RemotingEvent(MYCALL_SUCCESS,res_array,true);
                 dispatcher.dispatchEvent(rEv);

            }
            catch (e:Error)
            {
                  dispatcher.dispatchEvent(new RemotingEvent(MYCALL_ERROR,null,false));
            }
        }
        private function onLookUpError(fe:Object):void
        {
            netCon.close();
           dispatcher.dispatchEvent(new RemotingEvent(MYCALL_ERROR,null,false));
        }

This example demonstrates how you can use a defined class to package and send information in a uniform, predictable way.  It is true that ActionScript 2.0 allowed you to add additional parameters to your broadcastMessage call, but that means that every function signature is then coupled to the broadcast specifically.  Using a custom event class is a cleaner way to extend our events.

Extending EventDispatcher

If you are not dealing with DisplayObject classes but you still need to dispatch events you can usually just make your class extend EventDispatcher.  That will give you all the functionality to dispatch events from the new class.  However, because ActionScript 3.0 doesn't allow multiple inheritance, extending EventDispatcher doesn't always work.  You may need to dispatch events in a class that has to inherit from something already.  In those cases you can do what the above example did.  You add an EventDispatcher object to the class and then wrap it with the functions to get what you need.

 

/*other imports needed for remoting which is outside the scope of this tutorial*/
import flash.events.EventDispatcher;
public class MyDataTool extends SomethingElse
{

     private var dispatcher:EventDispatcher;
/*Other variables and items for flash remoting that are not part of this tutorial scope*/

     public function addEventListener(e_name:String,func:Function):void
        {
            dispatcher.addEventListener(e_name,func);
        }
        public function removeEventListener(e_name:String,func:Function):void
        {
            dispatcher.removeEventListener(e_name,func);
        }
//now you have some of the code like the above example that uses dispatcher.

}

Organization

A fully event driven system is great for a lot of other reasons.  It provides a way for your modules to communicate to parents or siblings while being agnostic to the details.  You could for example, have a navigation object that is populated dynamically.  The navigation object's class can define how it builds the information, and how the object animates when rolled over or clicked.  The nice thing is that the navigation object doesn't need to have any information about what logic happens when one of its choices is clicked. 

The correct way to design this type of behavior is to let the navigation class code deal with its presentation and behavior, and then dispatch a click event.  By doing this, you could use the same navigation class in multiple instances.

You may have one navigation that presents different products.  When the user clicks on one, the system displays dynamically populated price information for that selected object.  In another case, the same exact navigation class could create an object that displays a collection of greetings to begin a letter with.  Clicking a choice updates the displayed letter.

By separating the functionality at this level, we now have a reusable module that can be dropped into a wide variety of uses.  All we need to do is use the events it broadcasts to then do the unique behavior required in any scenario.

Bubble Events

Bubbling events allows you to have even greater flexibility.  In flash, an event will not bubble by default.  When an event does not bubble, it will either be caught by a declared listener or it will fizzle with no effect.  If it is set to bubble, then it will be caught by any explicit listeners, but it will also move up the display tree and be caught by anything else listening for that event name.

It is very important to note that bubbling only works if you are dispatching an event from an object that resides in the display tree.  If you never add something as a child to a DisplayObject (that in turn has been properly added to its parents all the way up to the stage) then bubbling will have no effect.

So you may be asking, why would I ever want to bubble events?  The reason is that you often will want something at the root level (or very near that) to respond to happenings many levels deep inside the application.  Rather than having to catch an event at every level and then dispatch the event yet again, you can just let it bubble up on its own.  This saves you lots of code requiring you to add listeners and declare event functions that only dispatch the same event again.  For an example of this in application see the tutorial on navigation control using ActionScript 3.0.

Final Thoughts

The robust event model introduced in ActionScript 3.0 is a huge improvement over the functionality that exists in ActionScript 2.0.  We now have an event model that is consistent with the object oriented design patterns that facilitate the design and creation of more complex and robust applications. 

Add to Technorati Favorites

No Comments

LEAVE A COMMENT

 
*