Example use of Event bubbling
A few days ago a good friend of mine Ruben and I were talking about Events. Ruben was putting together a post about events. While we we’re talking about it we got into a discussion about bubbling events and cases where it is appropriate. You should definitely check out Ruben’s post he makes good points and forwards you on to some good resources.
I’m just going to continue on with a conversation we were having while he was writing his post. Hopefully you have read Ruben’s post as well as the adobe documentation on event handling since I’m assuming you know what event bubbling is and how to work with it.
So how about we put together a situation? We are making a web site. One of our goals is to allow flexibility in the look and feel as I guess it always is right? Here is the approach we are taking.
We have a Main file which creates an instance of our site’s controller. The Main class adds the view of our site to the root display list and then runs our site. We will take all of this a little further by saying that the site’s controller has a navigator property (which we won’t discuss) that is used to navigate between the pages of your site and contains the current page being viewed. The role of the site controller then is to prepare the different elements of the site, the screen, the navigator, the pages, and to facilitate what communication is to happen between them.
We are going to leave out a lot of the boring details since we are talking about a pretty specific piece of this whole system. One thing that is assumed is that when you run the site it builds the landing page and then navigates to it. All user interface elements of the site exist within the site’s screen.
In our look at this website we are focusing on one section, how the UI interacts with the controller. If you take a look back at what the role of our site’s controller is you will notice that all we really need to communicate with is the navigator, since all of the look and feel details are part of the page system and not part of the site’s controller.
What we are going to use to communicate between the user interface elements on the screen and the navigator of the site is… you guessed it, events and more specifically events that bubble. All we are trying to achieve is this, when any user interface element triggers any kind of navigation we need to know. I have put together some example files to illustrate these points. In our site we will implement a Link class which is what determines where a navigation leads to and a LinkEvent which is how a ui element triggers a navigation.
The Link and LinkEvent classes are really simple so I will list them first
package
{
public class Link
{
public static const INTERNAL:String = "internal";
public static const EXTERNAL:String = "external";
public static const DOCUMENT:String = "document";
public var type:String;
public var path:String;
public function Link( type:String, path:String )
{
this.type = type;
this.path = path;
}
}
}
package
{
import flash.events.Event;
public class LinkEvent extends Event
{
public static const LINK:String = "link";
public var link:Link;
public function LinkEvent( type:String, link:Link )
{
this.link = link;
super( type, true, false );
}
}
}
I have to take a second to talk about the LinkEvent, after all it is what all of the fuss is about. This event is simple, you create it by passing it it’s type and the link which it will carry. We set in the constructor that the event will bubble, and it will not be cancelable. That is this line
super( type, true, false );
Now it is the role of our site’s controller to capture these link events, validate them, and then hand them off to the navigator. The navigator will take the appropriate actions for either navigating to a link internally, opening up the link in the appropriate window, or passing the link off to handle prompting the user to download some document. We have already set our self up here for success as this lays a foundation for deep-linking and integrating back/forward.
package
{
import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
public class SiteController
{
public var screen:DisplayObjectContainer;
public var navigator:Navigator;
public function SiteController()
{
createNavigator();
createScreen();
}
public function run():void
{
}
private function createScreen():void
{
screen = new Sprite();
screen.addEventListener( LinkEvent.LINK, handleLink );
}
private function createNavigator():void
{
navigator = new Navigator();
}
private function handleLink( event:LinkEvent ):void
{
navigator.navigateToLink( event.link );
}
}
}
Remember that all ui elements exist as children of the screen. Since we know that LinkEvent objects will always bubble and will not be cancelable we can just listen for them from the screen. Events bubble up through their ancestor display object containers, and so will always make it to the screen. All that is really left now is to talk about the Main file and to take a gander at our useless Navigator.
As we have said a few times the Main class simple creates an instance of the site controller, adds it’s screen to the root display list, and then runs the site.
package {
import flash.display.Sprite;
public class Main extends Sprite
{
public function Main()
{
var site:SiteController = new SiteController();
addChild( site.screen );
site.run();
site.screen.addChild( new ExampleUIElement() );
}
}
}
When our Navigator receives an event it simply outputs that it did so.
package
{
public class Navigator
{
public function navigateToLink( event:Link ):void
{
trace( "Received Link: " + event );
}
}
}
In the example main file we create a simple ui element which broadcasts a link event when it has been clicked, typical of something a UI element would do. The ExampleUIElement is listed below.
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class ExampleUIElement extends Sprite
{
private var link:Link;
public function ExampleUIElement()
{
link = new Link( Link.EXTERNAL, "http://www.google.com" );
draw();
listen();
}
private function listen():void
{
addEventListener( MouseEvent.CLICK, click );
}
private function click( event:MouseEvent ):void
{
dispatchEvent( new LinkEvent( LinkEvent.LINK, link ) );
}
private function draw():void
{
graphics.beginFill( 0x0, 1 );
graphics.drawRect( 0, 0, 100, 100 );
graphics.endFill();
}
}
}
This is just one example, I’m not saying that this is the only way to use event bubbling. It is simply just a time where bubbling is convenient.
August 5th, 2007 at 9:52 pm
“I have to take a second to talk about the LinkEvent, after all it is what all of the fuss is about. This event is simple, you create it by passing it it’s type and the link which it will carry. We set in the constructor that the event will not bubble, and it will not be cancelable.”
So did you mean to say “will bubble”?
That’s actually a really good idea, to use bubbling for dispatching custom events to a controller. I’m thinking about how this can apply to a cairngorm implementation, and it might fix one of the things that bothers me the most about cairngorm: the way they their views dispatch to their controllers.
August 6th, 2007 at 4:02 am
Awesome man! Glad to see you’re back writing posts again!
August 6th, 2007 at 7:02 am
@Steve - yeah you’re right Steve thanks for catching that. I’d love to see some Cairngorm examples if you get some time
@Ruben - thanks man, I needed to do something, after lost createage I just stopped all together.
August 6th, 2007 at 2:46 pm
Well, I haven’t actually applied cairngorm to a project yet, just been reading up on it.
I find that this is a great overview of the cairngorm microarchitecture though.
http://www.cairngormdocs.org/tools/CairngormDiagramExplorer.swf
In the case I was describing, your approach would replace the EventBroadcaster object, which is basically just a global dispatcher.
August 6th, 2007 at 8:48 pm
Thanks for the link! Ben’s project is using it right now. I’m interested though, will try to look into it sometime soon. :-S Whenever that may be, hehe.