views:

928

answers:

7

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.

+1  A: 

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.

Glenn
I found it very comforting to read, "Neither is better than the other. It's mostly just preference." :)That Delegate class is interesting. I've realized, this question seems to be a question of elegance. Going to update the tags.
Daniel Carvalho
+2  A: 

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.

Allan
+1  A: 

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.

Amarghosh
+1  A: 

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.

Richard
That's an interesting take I never considered, linking a MovieClip to a custom class. Creating a class for it sounds like a good idea. Although I haven't created a class for it, it is somewhat — although not as much as your idea — formalized. I do need to keep track of extra information, more than use a url as per the examples I gave, and I create an object on the fly that has many different properties storing various values. The only reason I wouldn't like to link the MovieClip to a class, is I generally like to minimize all dependencies on the .fla file, controlling everything via code.
Daniel Carvalho
I think being able to link MovieClips to Custom classes is one of the big strengths of flash. You get the best of both the flash designer and the flexibility of code.
Richard
However, if you're not using the flash designer to make your original loadImage MovieClip then you can encapsulate all the logic that is making it into the class and instantiate it in your code.
Richard
Yeah you're right, it would be playing into Flash's big strengths.
Daniel Carvalho
A: 
Hi,

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....

Sudheer
Yeah, I see what you're doing there. Using a combination of techniques, but, as you said, it's just another way of using anonymous functions.It feels long winded to me. I think each method, to extend or to Leprechaun is great individually, but combined together feels like extra unnecessary effort, with the sacrifice of being harder to read at a glance.
Daniel Carvalho
A: 

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.

Reuben
A: 

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.

Joel Hooks