Using the Event Manager

On a number of recent projects, I’ve been unhappy with the tight coupling I’ve needed between components in an application, to get the effective communications I needed. I set out in search of a way for any one component to subscribe to the events of any other component, without the need for the components to know about each other. To this end, I’ve developed the EventManager, which is documented here. I am working with John Bennet of the Macromedia Consulting team on integrating this with their FAST toolkit.

EventManager

Overview

This EventManager class was written to help developers build applications with a loosely coupled architecture. Any component in an application can broadcast an event to the EventManager, and other component can listen to the Event manager for that event. This removes the need for complex event chaining, which would otherwise be required to pass data from one deeply nested component to another.

In this diagram, Main.mxml has 2 children, ChildA and ChildB. ChildB has a child component, ChildB1. Using the traditional flex model, in order for ChildB1 to receive data from an event in ChildA, we could either tightly couple them together (a really bad idea) and explicitly add ChildB1 as an event listener for the event in ChildA, or we could use event chaining, and have Main listen for the event From ChildA, main could then in turn pass the data from the event to ChildB, and ChildB in turn could pass it on to ChildB1.

.

The EventManager offers an easier and cleaner alternative. If ChildA broadcasts its event to the EventManager, ChildB1 can listen directly to the event manager for that event. In this model, there is no direct dependency for Main or ChildB to be involved in the transaction, allowing for a truly loosely coupled architecture.

Using the Event Manager

There are 2 public methods of the EventManager. getEventManager() returns an instance of the EventManager. This step is required of any components wishing to broadcast or listen to the event manager. addEventListener() is the method mixed in from the built-in EventDispatcher class, which allows for any object to listen to any other object for events. This is used both to register the EventManager as a listener to the broadcasting component, and to register the listening component as a listener for the Event Manager. The following example shows this in action.

Main.mxml

<?xml version="1.0" encoding="iso-8859-1"?>
<mx:Application 

    width="800" height="600" 

    xmlns:mx="http://www.macromedia.com/2003/mxml"  

    xmlns:c="*">

    <c:ChildA />

    <c:ChildB />

</mx:Application>

 

ChildA.mxml

<?xml version="1.0" encoding="iso-8859-1"?>

<mx:Box 

    xmlns:mx="http://www.macromedia.com/2003/mxml"  

    creationComplete="appInit()">

    <mx:Model id="theData">

        <person>

            <name>Jeff</name>

            <city>Astoria</city>

        </person>

        <person>

            <name>John</name>

            <city>Newton</city>

        </person>

    </mx:Model>

    

    <mx:Script>

        <![CDATA[

        var em:EventManager;

        function appInit(){

            em = EventManager.getEventManager();

            this.addEventListener("dataSelected",em);

        }

       

        function sendChangeEvent(event:Object){

            var o:Object = new Object();

            o.type = "dataSelected";

            o.selectedData = event.target.selectedItem;

            this.dispatchEvent(o);

        }

        ]]>

    </mx:Script>

    <mx:DataGrid id="foo" dataProvider="{theData.person}" change="sendChangeEvent(event)" />

</mx:Box>
 

ChildB.mxml

<?xml version="1.0" encoding="iso-8859-1"?>

<mx:Box 

    xmlns:mx="http://www.macromedia.com/2003/mxml"  

    xmlns:c="*"

    backgroundColor="#FFFFFF" width="400">


 



    <c:ChildB1 />

</mx:Box>

 

ChildB1.mxml

<?xml version="1.0" encoding="iso-8859-1"?>

<mx:Box 

    xmlns:mx="http://www.macromedia.com/2003/mxml"  

    creationComplete="initApp()">

    <mx:Script>

        var em:EventManager;

        var selectedPerson:Person;

        function initApp():Void{

            em = EventManager.getEventManager();

            em.addEventListener("dataSelected",this);

        }

        function dataSelected(event):Void{

            selectedPerson = event.selectedData;

        }

    </mx:Script>

    <mx:Label text="{selectedPerson.name}"/>

    <mx:Label text="{selectedPerson.city}"/>

</mx:Box>


This example shows a reification of the components shown in the image above. Main.mxml instantiates two children, ChildA and ChildB. ChildB does nothing but instantiate ChildB1.

ChildA gets a handle on the EventManager with the static getEventManager method, and registers it to listen for the dataSelected event. When a user chooses an item from the datagrid, the event object is created and dispatched.

ChildB1 also gets a handle on the EventManager with the static getEventManager method. ChildB registers itself as a listener for the dataSelected event from the EventManager.

With this structure, the event data from ChildA can be broadcast and heard from ChildB1 without needing Main and/or ChildB to pass it along.

Source Code

import fast.faultHandler.FaultHandler;
import mx.events.EventDispatcher;

/**

    Provides framework for loosely coupled architecture.  
    This Singleton can receive events dispatched from anywhere 
    within the application, and will redispatch the events to 
    any listening component. 

*/

class EventManager {

    static var instance: EventManager;
    var dispatchEvent:Function;
    var addEventListener:Function;
    private function EventManager(){

        EventDispatcher.initialize(this);

        if (EventManager.instance==undefined) {

            EventManager.instance=this;

        } else {

            var message = "EventManager constructor called more than once.  
            EventManager is a singleton, therefore an Application can have at most one EventManager.  
            Try using EventManager.getEventManager() instead"

	        FaultHandler.show(message);

        }

    }

    public static function getEventManager(){

        if(EventManager.instance == undefined){

            EventManager.instance = new EventManager();

        }

        return EventManager.instance;

    }

    private function handleEvent(evtObj):Void{

        evtObj.dispatchingObject = evtObj.target;

        this.dispatchEvent(evtObj);

    }

    function toString():String{

        return "EventManager";

    }

}

Future Enhancements

    Possible addition of Event Queuing
  • Ability to rename events

There are no comments.

Leave a Reply