Creating Default, Cancelable Event Handlers

| 4 Comments | No TrackBacks

Events play a key role in Flex-based RIAs so it's important to have a solid understanding of them when building applications. Today I'm going to show you how to create a default event handler that performs an operation, and what steps are needed to cancel that default behavior to prevent it from happening.

A default event handler is a method of a component that handles the same event it dispatches and performs some default action. This happens a lot in component development. A component can dispatch an event, but also act on that event with some default behavior. You don't have to explicitly listen for the event because the default action happens automatically within the component itself.

For example, let's think about a "closeable" tab navigator. When you click on the "x" to close a tab, a "closeTab" event might be dispatched to signal that a tab is intended to be closed. The tab navigator itself listens for the "closeTab" event and provides a default behavior of removing the tab that was intended to be closed.

But what if you want to present the user with a confirm dialog first before the tab gets removed? In this situation, you don't want the default behavior to occur because the tab will be removed before the user has a chance to respond to the confirm dialog. So, you need a way to prevent the tab from being removed by the component.

There are 4 things associated with creating default event handlers and allowing them to be canceled. They are:

Rather than elaborating on these pieces with a few paragraphs of text, I think a code sample illustrates this best. Here's a sample component that has an "alarm" event with a default behavior of just showing an Alert dialog to let you know the default behavior has happened:

package
{

import flash.events.Event;

import mx.controls.Alert;
import mx.core.EventPriority;
import mx.core.UIComponent;	

[Event( name="alarm", type="flash.events.Event" )]

public class MyComponent extends UIComponent
{

	public function MyComponent()
	{
		// Assign a default event listener for the "alarm" event.  The key here
		// is the event priority of DEFAULT_HANDLER.  This is a low priority and 
		// allows normal listeners to execute first, giving them a chance to call
		// event.preventDefault() to cancel the event's default behavior
		addEventListener( "alarm", handleAlarm, false, EventPriority.DEFAULT_HANDLER, true );
	}
	
	/***
	 * Silly method here just to have the component dispatch an event that it
	 * has a default behavior for.
	 */
	public function triggerAlarm():void
	{
		// Create a new event of type alarm.  The key here is the third
		// parameter which signals that the event is allowed to be canceled
		// via event.preventDefault();
		dispatchEvent( new Event( "alarm", false, true ) ); 
	}
	
	/**
	 * The default behavior for handling the "alarm" event
	 */
	protected function handleAlarm( event:Event ):void
	{
		// Check to see whether or not the event had it's default behavior prevented
		// by another event listener
		if ( !event.isDefaultPrevented() )
		{
			// The event has NOT been canceled, so continue with
			// the default behavior
			Alert.show( "handleAlarm default handler executed from MyComponent" );
		}
	}
	
} // end class
} // end package

Here's a sample Flex application using this component. I add a listener for the "alarm" event, and if the user wants to cancel the default event behavior I call event.preventDefault() in my alarm listener:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" xmlns:local="*">
	
	<mx:Script>
		<![CDATA[
			
			protected function handleAlarm( event:Event ):void
			{
				// Just for example purposes, we have a flag in the UI to indicate
				// if we want to prevent the default event handler from executing
				// or not...
				if ( preventToggle.selected )
				{
					trace( "Default behavior is prevented" );
					event.preventDefault();
				}
				else
				{
					trace( "We didn't prevent the default behavior, so we'll see the alert from MyComponent." );
				}
			}
			
		]]>
	</mx:Script>
	
	<mx:Button label="Trigger Event" click="myComp.triggerAlarm();" />
	
	<mx:CheckBox id="preventToggle" label="Prevent default behavior?" selected="true" />
	
	<local:MyComponent id="myComp" alarm="handleAlarm( event );" />
	
</mx:Application>

So, there you have it! There's not much to it really, but it's a pretty useful technique. I'd actually like to see this covered a bit more in the Events portion of the livedocs site. Creating cancelable default event behaviors is a key part of Flex component development. Having a solid understanding of events will go a long way when it comes to building Flex applications.

Enjoy!

No TrackBacks

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

4 Comments

I bet you're not surprised to see me here. :) Very good info, and I was not aware of the EventPriority class, good to know. Just to confirm though, this approach does not provide a way to then execute the default behavior if, for example, the user confirms that they want to close the tab. Correct? How would you approach that scenario?

You would need to wrap the default behavior in a public method. Create a public method like "closeTabAt" and call it in the default handler -- Something like: closeTabAt( event.index ) if the event hasn't been canceled.

If the event *has* been canceled, then you can later call component.closeTabAt( ... ) on your own. As long as the public API is good, it's not an issue.

Great article Darron. Thanks! I'm just starting to learn AS3 (yes, I am very slow lol), and didn't know this existed.

I'll second what the last poster said, except that I've been using AS3 since it came out and I was still unaware of this. Very interesting.

Leave a comment



About this Entry

This page contains a single entry by darron published on January 29, 2008 9:15 AM.

Launching Firefox from ANT on OSX was the previous entry in this blog.

Hello World 2.0 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