Passing parameters to event handlers in AS3

| 10 Comments | No TrackBacks

In AS2 there were several different approaches to passing parameters to event handlers. In AS3, because of method closures there's no longer a need to use Delegate.create for scoping event handlers, which removes the opportunity to send parameters. How, then, can we pass extra arguments to the event handler?

The answer is to create your own custom event types. In AS3, Event is a actually provided to us as a class this time around (as opposed to a vanilla Object with a "type/target/data" properties). We can use this to our advantage, and create subclasses of Event that contain all of the information we want to send along to the event handlers.

Consider my number incrementing example for AS2. We passed the value we wanted to increment by as an extra parameter, and using a better implementation of Delegate (much like Joey Lott's Proxy class) this would be written as such:

// Pass the number 5 to handleIncrement so it knows
// how much to increment by
var handlerFunc:Function = Delegate.create( this, handleIncrement, 5 );

In AS3, what we'll do is actually create an IncrementEvent class. The class will extend Event (because an IncrementEvent is an Event). We'll give it an "amount" property to disignate how much to increment by:

// inside IncrementEvent.as
package {
	import flash.events.Event;
	
	// A event that contains extra information about how much
	// we need to increment by.
	public class IncrementEvent extends flash.events.Event {
		// Rather than use a string for adding event listeners, we use
		// a constant so we avoid typos
		public static const INCREMENT_TYPE:String = "increment";
		
		// The amount we need to incrememnt by
		public var amount:int;
		
		// Create a new event of "increment" type and store the
		// amount we need to increment by
		public function IncrementEvent( amount:int ) {
			super( INCREMENT_TYPE );
			this.amount = amount;	
		}
	}	
}

Now all that's left is to use this Event in an application. Below is some Flex2 markup/code to create an example showing the IncrementEvent in action.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application 
	xmlns:mx="http://www.macromedia.com/2005/mxml" 
	xmlns="*"
	childrenCreated="addListeners();">
	
	<mx:Script>
		<![CDATA[
			// Running total that we'll display in the text field
			private var value:int = 0;
			
			// When the app runs, we add listeners for the increment event so
			// we can update value whenever a button is clicked
			private function addListeners():Void {
				increment1.addEventListener( IncrementEvent.INCREMENT_TYPE, handleIncrement );
				increment5.addEventListener( IncrementEvent.INCREMENT_TYPE, handleIncrement );
			}
			
			// Update the value by adding the amount specified in the increment event
			private function handleIncrement( ie:IncrementEvent ):Void {
				value += ie.amount;
				display.text = value.toString();
			}
			
		]]>
	</mx:Script>
	
	<mx:HBox>
		<mx:VBox>
			<!-- Whenever a button is clicked, fire off an increment event with the
				appropriate incrememnt amount -->
			<mx:Button id="increment1" label="+ 1" click="increment1.dispatchEvent( new IncrementEvent( 1 ) );" />
			<mx:Button id="increment5" label="+ 5" click="increment5.dispatchEvent( new IncrementEvent( 5 ) );" />
		</mx:VBox>
		
		<mx:Label text="Value:" />
		<!-- Bind text to value so that whenever value changes we see it update in the UI -->
		<mx:Text id="display" text="0" />
	</mx:HBox>
	
</mx:Application>

Notice how the handleIncrement method now takes an IncrementEvent instead of plain old Event like it did for AS2. The IncrementEvent contains the extra information that we would've normally passed in a Delegate.create call.

Sure, it requires a bit more code to accomplish the same effect.. but in the end you wind up with more structure and the code communicates its intent much better (which is a basic idea of Extreme Programming).

To continue this example, I'll be adding an IncrementButton that will do away with the click handler and dispatching of the IncrementEvent. Instead, it will use an "increment" attribute, allowing us to write: <IncrementButton increment="5" label="+5" />, which is a cleaner implementation. Look for this in Flex By Example.

No TrackBacks

TrackBack URL: http://www.darronschall.com/mt/mt-tb.cgi/73

10 Comments

Surely that can't be the only way to do it? That seems long-winded and in some instances almost completely a waste of time. I've always had a love/hate relationship with actionscript and I think I still will, I'm hoping there is a better solution available, if not now then by the time AS3 gets it's formal release.

ah yes, i was thinking of doing something along the same lines as this. good job. the only thing that i was holding back on was changing the signature of the constuctor (it's completely different from the inherited class). do you think this method breaks any conventions?

I would bet that this isn't the only solution to the problem since programming gives us the freedom to do pretty much anything we want. I'll also agree that in this simple example, creating an event subclass like this is overkill. However, I would argue that it's best practice to create event classes for every type of event that you'll have in your system. Communicating intent with events not only makes your code easier to follow, but with AVM2 will make it execute faster. Vanilla Objects work as events, but there's a price to pay for the "looseness" of them.

In fact, it would've been much easier in this example to just write some code in the click attribute for "value += 1;" instead of dealing with the IncrementEvent, but my point was that event classes are going to play a key role in AS3 moving forward. Rather than trying to pass parameters to Delegate functions, you should think about creating new Events to listen to instead.

i definitely agree that events are the way to go; especially creating your own custom ones. now that we have the ability to make private classes in the same .as file it will make it much cleaner and easier to implement them.

you're right that this probably isn't the only solution, but i'm having a hard time coming up with anything else.

hmm... not sure i like the idea of having to whip up bunches of new classes but I guess you only need to write them once, right?

Or perhaps a wrapper class for making custom event listeners on the fly instead somehow?

nice. I was just looking into this today too.

well, in a lot of other languages, you do have a class for each event type, a class for each error type, exception type, etc. it may seem odd to start doing it in AS, but it's a common solution.

Very interesting, no doubt I'll be coming back to this again.

Darron, why is it not possible to go:

Thx

Stefan

hmm it chopped my code...
I'll try without brackets.

Why does this not work:

mx:Button id="increment5" label="+ 5" click="this.dispatchEvent( new IncrementEvent( 5 ) );"

Hi Stefan,

The reason is because "this" inside the click attribute refers to an instance of the class, and the class isn't registered to listen for the increment event so "nothing happens".

Leave a comment



About this Entry

This page contains a single entry by darron published on November 11, 2005 1:19 PM.

Announcing FlashVNC was the previous entry in this blog.

FlashVNC Released is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Archives

OpenID accepted here Learn more about OpenID
Powered by Movable Type 5.02