overview
v2 components offer many ways to handle events
this lecture explores the various techniques
discusses event handling best practice
responding to input
"handling an event" from a user interface component means mapping a user action to a program response
examples of user action:
in an OOP application, "program response" means invoking a method
examples of methods that produce a program response:
submitForm()
(sends information to a server)pauseVideo()
(stops a video at the current frame)to learn event handling techniques, we'll study a classic input/response example: clicking a "Submit" button to submit a form
from input to method call
as a best practise, each input event should be handled by a single method
for example, clicking the submit button should invoke OrderForm.submitForm()
many ways to handle an event, but the goal is always the same:
OrderForm Class Skeleton
our example is an order form with a single button
the form is represented by a custom OrderForm
class
the OrderForm
class:
OrderForm.submitForm()
to submit the formOrderForm
code listing:
import mx.controls.Button; class OrderForm { private var submitBtn:Button; private var status:TextField; public function OrderForm (target:MovieClip, depth:Number) { // Create submit button. submitBtn = target.createClassObject(Button, "submit", depth); submitBtn.label = "Submit"; // Create status text field. target.createTextField("status", depth + 1, 0, submitBtn.height + 10, 300, 200); status = target.status; status.border = true; } // Submits the form. public function submitForm ():Void { status.text += "Now submitting form...\n"; submitBtn.enabled = false; } }
now let's see how to connnect submit button clicks to OrderForm.submitForm()
calls
event handler functions
technique #1: define an event handler function on the component
event handler function takes the following form:
componentInstance.eventNameHandler = function (e:Object) {...}
eventName is the event being handled
e is an object containing information about the event
for example, e.target
is a reference back to the component that broadcast the event
so, to handle the our submit button's click event we use this basic approach:
submitBtn.clickHandler = function (e:Object) { }
inside the function, we want to invoke submitForm()
, so we try this:
submitBtn.clickHandler = function (e:Object) { submitForm(); }
but that doesn't work because a function nested in a method cannot access the methods or properties of the class within which it is nested
but, local varaibles are accessible to nested functions
hence, we can give clickHandler() access to the current object's methods by storing the current object in a variable
var theForm:OrderForm = this;
so here's the entire event handler function:
var theForm:OrderForm = this; submitBtn.clickHandler = function (e:Object) { theForm.submitForm(); }
and here's what it looks like in the context of our class:
class OrderForm { private var submitBtn:Button; private var status:TextField; public function OrderForm (target:MovieClip, depth:Number) { submitBtn = target.createClassObject(Button, "submit", depth); submitBtn.label = "Submit"; target.createTextField("status", depth + 1, 0, submitBtn.height + 10, 300, 200); status = target.status; status.border = true; // Event Handler Function var theForm:OrderForm = this; submitBtn.clickHandler = function (e:Object) { theForm.submitForm(); } } public function submitForm ():Void { status.text += "Now submitting form...\n"; submitBtn.enabled = false; } }
event handler functions discouraged
macromedia officially discourages the use of event handler functions
event handler functions are limited: only one handler can be defined at a time
only one handler means the click event cannot cause multiple separate program responses (e.g., a sound, an animation, and a form submission)
event handler functions supported for legacy reasons: v1 components used event handler functions
event listener functions
exactly like event handler functions except more than one can respond to the same event
to create an event listener function, define a standalone function, like this:
function submitClickHandler (e:Object):Void { theForm.submitForm(); }
then invoke addEventListener()
to register the function for the desired event
componentInstance.addEventListener(eventName, handlerFunction);
for example:
submitBtn.addEventListener("click", submitClickHandler);
later, if desired, stop handling the event with:
submitBtn.removeEventListener("click", submitClickHandler);
here's what an event listener function looks like in the context of our class:
class OrderForm { private var submitBtn:Button; private var status:TextField; public function OrderForm (target:MovieClip, depth:Number) { submitBtn = target.createClassObject(Button, "submit", depth); submitBtn.label = "Submit"; target.createTextField("status", depth + 1, 0, submitBtn.height + 10, 300, 200); status = target.status; status.border = true; // Event Listener Function var theForm:OrderForm = this; function submitClickHandler (e:Object):Void { theForm.submitForm(); }; submitBtn.addEventListener("click", submitClickHandler); } public function submitForm ():Void { status.text += "Now submitting form...\n"; submitBtn.enabled = false; } }
listener objects
event listener functions are still less flexible than event listener objects
just like listener functions, objects can register to receive events from a component
when an event occurs, the component invokes the appropriate event method on the registered object
event methods match the name of the event, for example:
benefits of listener object over listener function:
typed listener objects
a typed listener object is an instance of a class designed to handle an event
the class defines a method by the name of the event being handled
an instance of the class is registered to handle the component event
for example, this class handles a submit button click:
class SubmitButtonHandler { private var form:OrderForm; public function SubmitButtonHandler (theForm:OrderForm) { form = theForm; } public function click (e:Object):Void { form.submitForm(); } }
our OrderForm class registers a SubmitButtonHandler
as follows:
submitBtn.addEventListener("click", new SubmitButtonHandler(this));
of all v2 event handling techniques, typed listener objects require the most effort to create
but this approach is cleanest because:
current object listener
in simple applications, a typed event object might be overkill
instead, the current object can register to handle events directly
for example, our OrderForm
class could define a click()
method:
private function click (e:Object):Void { submitForm(); }
then OrderForm
would register itself as a click event listener:
submitBtn.addEventListener("click", this);
here's the code in context:
class OrderForm { private var submitBtn:Button; private var status:TextField; public function OrderForm (target:MovieClip, depth:Number) { submitBtn = target.createClassObject(Button, "submit", depth); submitBtn.label = "Submit"; target.createTextField("status", depth + 1, 0, submitBtn.height + 10, 300, 200); status = target.status; status.border = true; // Current object listener submitBtn.addEventListener("click", this); } public function submitForm ():Void { status.text += "Now submitting form...\n"; submitBtn.enabled = false; } private function click (e:Object):Void { submitForm(); } }
multiple event source conflicts
when a single class handles events for multiple components, event name conflicts can occur
for example, suppose our form has a "submit" button and a "reset" button:
// Current object listener submitBtn.addEventListener("click", this); resetBtn.addEventListener("click", this);
both button events invoke the same click()
method!
to distinguish the buttons, check the event source via the event object's target
property:
private function click (e:Object):Void { if (e.target == submitBtn) { submitForm(); } else if (e.target == resetButton) { resetForm(); } }
but above code is clumsy and hard to read when many components are involved
generic listener objects
generic object listeners help avoid multiple event source conflicts without added effort of creating typed event listener classes
create an Object instance:
var clickHandler:Object = new Object();
define an event method directly on it:
clickHandler.click = function (e:Object):Void { theForm.submitForm(); };
(above code is only allowed because Object
class is dynamic)
register the instance to handle the desired event:
submitBtn.addEventListener("click", clickHandler);
the generic listener object in context:
class OrderForm { private var submitBtn:Button; private var status:TextField; public function OrderForm (target:MovieClip, depth:Number) { submitBtn = target.createClassObject(Button, "submit", depth); submitBtn.label = "Submit"; target.createTextField("status", depth + 1, 0, submitBtn.height + 10, 300, 200); status = target.status; status.border = true; // Generic Event Listener Object var clickHandler:Object = new Object( ); clickHandler.click = function (e:Object):Void { theForm.submitForm(); }; submitBtn.addEventListener("click", clickHandler); } public function submitForm ():Void { status.text += "Now submitting form...\n"; submitBtn.enabled = false; } }
the case for a Delegate
all of the event handling techniques we've seen so far do the same thing:
OrderForm.submitForm()
in response to a button clickin other words, they "delegate" the event to the submitForm()
method
in ellipsis (flash updater, 7.2), macromedia formalized the concept of delegation with a new class: mx.utils.Delegate
Delegate.create()
forwards an event to a particular method
here's how to use Delegate.create()
to handle our submit button click:
submitBtn.addEventListener("click", Delegate.create(this, submitForm));
Delegate.create()
in context:
class OrderForm { private var submitBtn:Button; private var status:TextField; public function OrderForm (target:MovieClip, depth:Number) { submitBtn = target.createClassObject(Button, "submit", depth); submitBtn.label = "Submit"; target.createTextField("status", depth + 1, 0, submitBtn.height + 10, 300, 200); status = target.status; status.border = true; submitBtn.addEventListener("click", Delegate.create(this, submitForm)); } public function submitForm ():Void { status.text += "Now submitting form...\n"; submitBtn.enabled = false; } }
Delegate is the most convenient event handling technique, and is quite clean
event architecture weaknesses
no matter which technique you choose, all v2 component events suffer two important weaknesses
1) event listeners are not typed
2) event objects are not typed
e.trget
does not cause an errorsummary
as a best practice, handle events with either mx.utils.Delegate
or typed listener objects
when creating event-broadcasting classes, consider using Java-style delegation event model for better type safety