Passing parameters to a MouseEvent listener

Problem:

Here is how we construct a MouseEvent handler in ActionScript 3.0:

myButton.addEventListener(MouseEvent.CLICK, openURL, false, 0, true);

private function openURL(e:MouseEvent):void
{
   navigateToURL(new URLRequest("http://www.mysite.com"), "_blank");
}

Note that we set the fourth parameter, useWeakReference, to true. This ensures that if this listener is the the only reference to the myButton remaining, myButton can still be garbage collected if required.

The trouble with this is that every time myButton is clicked, the same URL will open. We can’t easily pass a parameter to the function which is acting as a listener. How can the same function be used by many buttons to open a different URL for each button? How can we pass a parameter?

Solution 1: Custom events (won’t work):

It would be nice if we could dispatch a custom event on mouse click. Custom events extend the event class enabling us to dispatch an event with additional properties (like a URL). This event is received by the handler then the property is accessed. The trouble is this approach won’t work here because the Flash Player automatically dispatches MouseEvents, we don’t dispatch them ourselves. (If anyone sees a way this may be done, please comment below.)

Solution 2: Embed the function call (not advised):

An interesting strategy is used here, whereby a call to a function is used as the event handler, and this returns the function to use, including the desired parameter. The trouble with this approach is that it is not really good form, in that there is no way we can reference the handler if we did want to remove it using removeEventListener().

We’re beginning to see that there is actually no way to “pass” a parameter, really. We have to rather detect the button-specific value by “association”. The following solutions all achieve this by using the MouseEvent’s target property:

Solution 3: Use a switch statement on the event’s target property

When the button is clicked, providing there is no further mouse-enabled object inside it, that button is the event’s target. We can say it dispatched the event. We can access the target object that dispatched the event using a property of the event itself:

myButton.addEventListener(MouseEvent.CLICK, openURL, false, 0, true);

private function openURL(e:MouseEvent):void
{
   trace(e.target + "dispatched this event!");
   navigateToURL(new URLRequest("http://www.mysite.com"), "_blank");
}

If we can access the target we can decide what to do in the handler depending on the target:

myButton.addEventListener(MouseEvent.CLICK, openURL, false, 0, true);

private function openURL(e:MouseEvent):void
{
   var u:URLRequest = new URLRequest;

   switch(e.target)
   {
      case myButton:
         u.url = "http://www.mysite.com";
         break;

      case myOtherButton:
         u.url = "http://www.myothersite.com";
         break;

      default:
         throw new Error("No URL set for " + e.target);
         return;
   }

   navigateToURL(u, "_blank");
}

This works well.

Solution 4: Use a public custom property inside the dispatching object

We could avoid a switch statement and instead have a property inside the dispatching class which can then be detected. So if our button extends Sprite, let’s say, we could have an instance property in there called url and we could detect that, as follows:

myButton.addEventListener(MouseEvent.CLICK, openURL, false, 0, true);

private function openURL(e:Event):void
{
   var u:URLRequest = new URLRequest;
   navigateToURL(new URLRequest(e.target.url), "_blank");
}

This is fine too, but we might need to throw an error if the object dispatching the event doesn’t have a url property.

Solution 5: Use a dictionary object to track variables

Dictionary objects in ActionScript are similar to associative arrays but they can accept complex objects, as opposed to strings, as their keys. After creating our button we could push a reference to it into a dictionary and specify a value to associate with it, like this:

var dict:Dictionary = new Dictionary(true);  // use weak references to the objects
var myButton:Sprite = new Sprite;
dict[myButton] = "http://www.mysite.com";
addChild(myButton);

myButton.addEventListener(MouseEvent.CLICK, openURL, false, 0, true);

private function openURL(e:MouseEvent):void
{
   navigateToURL(new URLRequest(dict[e.target]), "_blank");
}

This is perfectly workable too.

Solutions 3, 4 and 5 all seem equally good solutions to this problem. There is just one final option, which is to avoid the problem altogether by using separate listeners for each button. But that, of course, is what we were trying to solve in the first place.

Where to install SWCs for Flash CS4

ComponentsSomething we use here is the Google Maps API for Flash, which is delivered as an SWC file. SWC files are ActionScript classes which have been pre-compiled into an SWF. The fact they are pre-compiled means you don’t have to re-compile them every time you export your application. Another benefit of  SWCs is that they enable you to deliver functionality while keeping your source code obfuscated to some degree. In their instructions, Google say you should install the SWC files here:

C:\Program Files\Adobe\Adobe Flash CS3\language\Configuration\Components

What if you’re using Flash CS4? Well, in this version of Flash the ActionScript 3.0 Settings dialogue has changed (both the publish settings version and the global preferences version). You can specify a “library path” – this may be any folder on your computer. This is supposed to be a repository for your SWC files; you can set this and drop an SWC in here.

This will allow you to access the classes compiled into the SWC, however the SWC does not show up in the Components panel in Flash. If you are working visually in the Flash IDE and would like it to do so, you will need to forget about the library path option and instead drop SWCs into this folder:

C:\Program Files\Adobe\Adobe Flash CS4\Common\Configuration\Components

Restart Flash and the component should be there.

Cannot install Flash Player on Windows?

Problem

You’re having trouble installing the Flash Player Active X control on Windows. Every time you run the installer it quits, recommending an Adobe Technote. The trouble is this doesn’t solve the problem either. You’ve uninstalled the previous player, quit all apps, still no joy.

Solution

In this situation the cause is probably incorrect registry permissions. You can correct this by using Microsoft’s SubInACL tool. It is available here bundled with a script which will whip through your registry granting full permissions to administrators for the appropriate keys. You should then have no problem running the Flash Player installer.

Why use init methods in AS3?

Init methodsChecking our stats, we noticed that some people are searching for “why use init methods as3”.

There are actually several reasons to consider using init methods in AS3 rather than having all of your initialisation code in your constructor functions:

  • Constructor functions run slower than “regular” methods. (See here for details.)
  • You might want to re-run initialisation code without recreating the instance.
  • You might want to run delayed initialisation code, for example once an object has been added to the Display List. (See our post here regarding this issue.)

getDefinitionByName() produces ReferenceError: Error #1065

Scenario

Something we often do here is get the “id” of a clicked menu button and instantiate a section of a site, depending on the ID passed. Usually all sections of a site inherit from a generic “Section” class, then have additional specific capabilities depending on the section. So, depending on the ID passed, we need to create an instance of the particular class.

This means we need to get the class name dynamically from the string passed by the button.

Let’s imagine the “music” button was pressed. We’ll either have a listener for this or we’ll directly call a method called loadSection(), passing the ID (the name of the button usually). We name instances in camel notation, so that’ll be “music”.

This string goes over to the loadSection() method and in here we use a custom StringUtils class to convert the first letter to upper case, getting us the required class name as a string. So this gives us “Music”. A string is no good on its own though – we need to get the class reference for this, as follows:

// required "dummy" ref to ensure class is compiled...
var dummyRef:Music;
...
// get class reference
var classRef = getDefinitionByName(id);
// instantiate class instance as current section
section = new classRef();

Problem

All well and good. The trouble is sometimes it just doesn’t work and you’ll get:

ReferenceError: Error #1065: Variable Music is not defined

There’s a thread here where the person was getting this, and it drifted off into another debate without being solved. The import statement is there. The dummy class reference is there, but still it doesn’t work.

Solution

What is not immediately clear is that you need to provide a fully qualified class path to getDefinitionByName() – even though you have set up an import.

Amend as follows (for example) and your problem is solved:

// var classRef = getDefinitionByName(id);
var classRef = getDefinitionByName("sections." + id);

“sections.” here is a reference to our package structure.

We hope this helps you.