10
votes

How do you pass parameters / variables through event listeners? I've overcome this problem fairly effectively using anonymous functions; which is an incredibly simple solution, but at the end of the day, it smells like a giant loophole for functionality that I feel should be provided natively anyways.

Normally life would go on, but as fate would have it, now I actually need to remove the listener, and doing so when you've using an anonymous function is a bit funky. So, once again, I'm trying to figure out how to pass parameters through to event listeners so that the event listeners can be removed by simply referencing the function.

As odd as it may seem, I've overcome this issue as well, BUT, I don't like it and tired of using it. In my opinion it is code smell. But it works like a charm. I store the variable, object or whatever on the dispatching MovieClip. So, if I'm looping though an array of data, generating thumbnails on the fly, I simply store the data variable (normally an object with multiple properties) in the actual thumbnail MovieClip. Then I can access all the data in an event listener method by referencing:

event.target.data
. In this example, "data" is the name of the variable holding holding the information I want. Because another issue that crops up when I don't use this is, is that when I'm looping through an array and generating thumbnails that click to view large images, the index is not consistent. At the end of the loop, ALL the thumbails will all open an image using the last index of "i". So if I had an array with a length of 12, they'd all load the 12th image regardless of what thumbnail you clicked on. Storing the data into the MovieClip itself, creates a solid reference that never changes.

This has been bothering me for some time now. Basically what I want to know is, is this good practice, are there better solutions out there?

Below are some diet examples. I can post more detailed examples if necessary. All examples depict a thumbnail that loads a large image when clicked.




Without the use of an anonymous function (the problem):

tempThumb.addEventListener(MouseEvent.CLICK, loadImage);

public function loadImage(_event:MouseEvent):void  
{
    // I don't have the variable _url and preparing a hot bath with a cold blade  
}

Use of an anonymous function:

tempThumb.addEventListener(MouseEvent.CLICK, function(_event:MouseEvent) { loadImage("large.jpg"); } );

public function loadImage(_url:String):void  
{
    // I got the variable _url and packing away the razor blades  
}

Without the use of an anonymous function, but using my smelly Leprechaun technique of storing the data into the MovieClip dispatching the event

tempThumb.data = "large.jpg";

tempThumb.addEventListener(MouseEvent.CLICK, loadImage);

public function loadImage(_event:MouseEvent):void  
{
    trace(event.target.data);
    // I can access the variable  
}

I'm not clued up on programming terminology, so I've dubbed the above the Leprechaun Technique. Storing / hiding variables in objects for later use / access. It solves all my problems and works amazingly well. But, if there is a better way to do it, I want to know that way.

8

8 Answers

2
votes

Yes I know what you mean. What I do is to extend the event and so dispatch a custom event. This way I can create properties in the custom event that contain the necessary data. It seems clean but does require a bit of effort. It is not ideal but I don't know of a significantly better way.

1
votes

You can use what's called "Delegates" if you don't like storing the data in the target member. Neither is better than the other. It's mostly just preference.

Here's an example of a Delegate class (nothing built-in): http://www.actionscript.org/resources/articles/205/1/The-Delegate-Class/Page1.html

That's probably the best idea if the eventdispatcher isn't your own class. Otherwise, storing the variable in your own class isn't a bad idea at all. Just OOP style.

Cheers.

1
votes

Anonymous functions are better avoided as they can make the code clumsy and sometimes lead to memory leaks. I would use custom events if I am responsible for dispatching the event too, but that is not the case here.

In this case, I would store the URLs and movie clips in two separate class level arrays and use

var url:String = urlArray[mcArray.indexOf(event.target)];

inside the event handler to get the URL.

Not that there is anything wrong with storing the data in the movieclip, but I don't feel that it's a good way to go. But following best practices even when they make things difficult is not a good idea either - the choice is yours.

1
votes

If the MovieClip needs to keep track of extra information that you need to know when it's been clicked, then I would use a more formalised version of your Leprechaun technique.

I'd create a new class called something like SpecialImage that extends MovieClip and has a property for storing the url to load.

Depending on how your SpecialImages are created you can either create them in code or link a MovieClip in your library to this class.

It's basically a posh leprechaun, but as the information is directly related to the image MovieClip, storing them together feels like the best thing.

1
votes

(I think you'll want to read this answer.)

Regarding your doubt, you felt it yourself: your leprechaun's smelly (pun). Imagine it's a Sprite instead of a MovieClip. It isn't dynamic, so you can't add properties unless you do like Richard said.

Here's the better solution you wished, based on your example:

tempThumb.addEventListener(MouseEvent.CLICK, loadImage("large.jpg"));

public function loadImage(_url:String):Function {
  return function(_event:MouseEvent):void {
    // Now you have both variables _url and _event here!
  }
}

But you want to be able to remove listeners, so:

var functionLoadImage:Function = loadImage("large.jpg");
tempThumb.addEventListener(MouseEvent.CLICK, functionLoadImage);

public function loadImage(_url:String):Function {
  return function(_event:MouseEvent):void {
    // Now you have both variables _url and _event here!
  }
}

//trace(tempThumb.hasEventListener(MouseEvent.CLICK));
tempThumb.removeEventListener(MouseEvent.CLICK, functionLoadImage);
//trace(tempThumb.hasEventListener(MouseEvent.CLICK));
0
votes

You can do the following, which basically is a cleaner way of passing custom data.

example code:
public function myCustomEvenListener(e:MouseEvent,myCustomData:Object)
{
//Do whatever you wnat with myCustomData Object here...
}
//this is the function where you add event listeners to components..
public function init():void
{
var myCustomObject:Object=new Object();
myCustomObject.url="http://xyz.com/image.jsp";
myCustomObject.anydata="You can Add anything here";

mycomponent.addEventListener(MouseEvent.CLICK,function (e:MouseEvent) : void {
                  myCustomEventListener(e,myCustomObject);
            });

}

This is just another way of using anonymous functions though....

0
votes

It is probably better practice to make a custom class that extends MovieClip for your thumnbnail, and have that thumbnail dispatch a custom Event class that has a place to store your data:

Make a custom URLEvent that can store the url property along with the event

package{

import flash.events.Event;

public class URLEvent extends Event{

    public var url:String;
    public static const CLICK:String = "URLEventClick"

    public function URLEvent(type:String, url:String = "", bubbles:Boolean = false, cancelable:Boolean=false){
        super(type, bubbles, cancelable);
        this.url = url;
    }

    override public function clone():Event{
        return new URLEvent(type,url,bubbles,cancelable);
    }
  }
}

Make a custom thumbnail class that can store the url variable, and sends a URLEvent when clicked:

package{

import flash.display.MovieClip;
import flash.events.MouseEvent;

public class Thumbnail extends MovieClip{

    protected var url:String;

    public function Thumbnail(url:String){
        this.url = url;
        addEventListener(MouseEvent.CLICK,clickHandler);
    }

    //the thumbnail captures its own click and dispatches a custom event
    protected function clickHandler(event:MouseEvent):void{
        dispatchEvent(new URLEvent(URLEvent.CLICK,url));
    }

}

Now you can use your Thumbnail class to load each image:

tempThumb.addEventListener(URLEvent.CLICK,tempThumbClickHandler);

function tempThumbClickHandler(event:URLEvent):void{
//we can get the url property direct from the URLEvent
    loadImage(event.url);
}

This method is much better OOP practice, as you do not need to know what type of object your event.target is - you know from the event type exactly what type of data will be sent with each event.

0
votes

If I were looking for this functionality, and I often do, I would make tempThumb a Class that carried the url property. then:

MyThumbClass(event.target).url //in the MouseEvent.CLICK handler

Strong typing, easy access, clean syntax, low complexity.