We tend to build many Flex applications with modules, so having the ability to broadcast and listen for messages based on the scope attribute for both Dispatchers and EventHandlers is essential in Swiz.
UPDATE: Part 2 has Swiz framework code changes and a more robust solution.
If you don’t know about Swiz Event Scoping, then read on; otherwise, skip to the section labeled Filtering With Topics below.
Background
You can read the actual documentation from Swiz in the last link, but the gist is that you can tell Swiz to either broadcast events on a “global” level (using the highest level Application object) or on a “local” level (using the Module itself) and then listen to those events in the same fashion. This allows you to tell modules to listen to events only from themselves and not other modules (that might have the same events) or to listen to all events from your main, shell application and/or all other modules. Let’s look at a quick example by assuming the dispatching code is in LoginViewMediator and the handling code is in LoginController in a Module.
Local Event Dispatching
Local Event Dispatching From LoginViewMediator
/**
* Allows this class to dispatch local events. Only methods using an
* EventHandler metatadata tag with the property of scope="local" can
* handle this event.
*
* <p>
* The event dispatcher is injected by Swiz due to the [Dispatcher] metadata
* and the class member's type of IEventDispatcher.
* </p>
*/
[Dispatcher(scope="local")]
public var localDispatcher:IEventDispatcher;
...
public function login(userName:String, password:String):void
{
logger.debug("login");
var appEvt:AppEvent;
appEvt = new AppEvent(AppEvent.SERVICE_LOGIN);
appEvt.username = userName;
appEvt.password = password;
this.localDispatcher.dispatchEvent(appEvt);
}
Local Event Handling in LoginController
[EventHandler(event="AppEvent.SERVICE_LOGIN", properties="username, password", scope="local")]
public function login(username:String, password:String):void
{
logger.debug("login: username = " + username + ", password = " + password);
}
If we had other Modules with this same EventHandler statement in their LoginController they would not fire because we specified the scope as local. If we change the scope property to global and the AppEvent is in a common library that all Modules can use, then they would each hear this event and all fire, which in many cases is not what you want. Just so we have a full understanding of what’s going on, let’s show an example of broadcasting global events in a similar fashion:
Global Event Dispatching
Global Event Dispatching From ModuleA
/**
* Allows this class to dispatch global events. Only methods using an
* EventHandler metatadata tag with the property of scope="global" can
* handle this event. This means other modules and even the shell application
* can handle this event if we want.
*
* <p>
* The event dispatcher is injected by Swiz due to the [Dispatcher] metadata
* and the class member's type of IEventDispatcher.
* </p>
*/
[Dispatcher(scope="global")]
public var globalDispatcher:IEventDispatcher;
...
public function sayHello():void
{
logger.debug("sayHello");
var appEvt:AppEvent;
appEvt = new AppEvent(AppEvent.SAY_HELLO);
appEvt.hello = "Hello World!";
this.globalDispatcher.dispatchEvent(appEvt);
}
Global Event Handling in Module B
[EventHandler(event="AppEvent.SAY_HELLO", properties="hello", scope="global")]
public function hello(hello:String):void
{
logger.debug("hello = " + hello);
}
Again, this is awesome…but what if you want to broadcast a message that you only want some of the modules to hear? You could obviously put in some simple logic in the event handler that determines if your Module was supposed to listen to the event based on a parameter passed in the EventHandler MetaData, but this could potentially become a large and unwieldy task that you’ll need to apply to all new modules…so what if we introduce filtering of Swiz events via a topic attribute in the EventHandler metadata and a corresponding topic property in our dispatched event?
To do this, we’ll need to do some quick monkey patching to Swiz (by creating the same package structure and adding the same classes we want to change that exist in the Swiz SWC). First, let’s add the topic property to the org.swizframework.metadata.EventHandlerMetadataTag. Add the topic property:
protected var _topic:String;
public function get topic():String
{
return _topic;
}
Then add the following to the method override public function copyFrom( metadataTag:IMetadataTag ):void
if( hasArg( "topic" ) )
_topic = getArg( "topic" ).value;
Next we’ll edit the method public function handleEvent( event:Event ):void in org.swizframework.utils.event.EventHandler. Add the following right after the first if() statement:
// look for a topic -- if the one exists in the EventHandler MeataData and
// the event does not have one or the value does not equal the EventHandler's
// then we'll filter out this event and not allow it
if(metadataTag.topic != null)
{
if(event["topic"] == null)
{
return;
}
else if(event["topic"] != metadataTag.topic)
{
return;
}
}
That’s it. Let’s give it a whirl by using a global dispatcher again, but adding a topic property to the event and a topic attribute to the EventHandler metadata.
Global Event Dispatching From ModuleA
/**
* Allows this class to dispatch global events. Only methods using an
* EventHandler metatadata tag with the property of scope="global" can
* handle this event. This means other modules and even the shell application
* can handle this event if we want.
*
* <p>
* The event dispatcher is injected by Swiz due to the [Dispatcher] metadata
* and the class member's type of IEventDispatcher.
* </p>
*/
[Dispatcher(scope="global")]
public var globalDispatcher:IEventDispatcher;
...
public function sayHello():void
{
logger.debug("sayHello");
var appEvt:AppEvent;
appEvt = new AppEvent(AppEvent.SAY_HELLO);
appEvt.hello = "Hello World!";
appEvt.topic = "wasiTopic";
this.globalDispatcher.dispatchEvent(appEvt);
}
Global Event Handling in Module B
[EventHandler(event="AppEvent.SAY_HELLO", properties="hello", scope="global", topic="wasiTopic")]
public function helloWithTopic(hello:String):void
{
logger.debug("hello = " + hello); // WORKS
}
[EventHandler(event="AppEvent.SAY_HELLO", properties="hello", scope="global")]
public function helloWithOutTopic(hello:String):void
{
logger.debug("hello = " + hello); // NOPE
}
[EventHandler(event="AppEvent.SAY_HELLO", properties="hello", scope="global", topic="fooTopic")]
public function helloWithOutTopic(hello:String):void
{
logger.debug("hello = " + hello); // NOPE
}
UPDATE: Part 2 has Swiz framework code changes and a more robust solution.