views:

21

answers:

1

Hello all,

I have a strange issue! I am trying to remove an event listener on a FileReference object by calling a function, but it seems not to be removed, and I do not understand why.

Here is the code:

private function clearFileUploadListeners(file:FileReference, index:String):void {
    var dispatchEvent:Function = function(event:Event):void {
        dispatch(event.type, event, index);
    };

    file.removeEventListener(Event.COMPLETE, dispatchEvent);
    var bool:Boolean = file.hasEventListener(Event.COMPLETE);
    if (bool)
        trace("ERROR");
}

When I run this code, the trace actually happens. I don't understand why this boolean returns true, when I just tried to remove the eventListener just above! I guess I am probably doing something really stupid because it seems like a strange error.

I hope someone can please help me on this issue.

EDIT:

I believe it has to do with the fact that the dispatchEvent function is defined inside another function when I add the listener:

private function upload(file:FileReference, index:String):void {
    var dispatchEvent:Function = function(event:Event):void {
        dispatch(event.type, event, index);
    };

    file.addEventListener(Event.COMPLETE, dispatchEvent);
}

The problem is that I need to access this "index" variable from the listener, and I can't set it as a global variable as each file has it's own index and it's a burden if I have to extend each event class to keep track of the index (Event, ProgressEvent, ..). I hope someone can please help me on this.

EDIT2:

I actually found a temporary solution, I am not sure if it is the best! I put my removeListener method actually inside the upload method, but made it a variable. As AS3 allows dynamic object, I attached this method to one of my object, and so I just call the reference to the method when necessary. The event is actually removed. Is this a good solution please?

Thank you very much, Rudy

A: 

You're right, it has to do with the fact that you're defining a function inside another function, then using it to handle events.

Each time the function upload is called, it creates a new closure, and assigns a reference to it to the dispatchEvent variable, which is then passed to the addEventListener class. So each time upload is called, it is using a new, different closure in the call to addEventListener. Similarly, in the clearFileUploadListeners function, a new closure is being created on each call (which happens to have the same code each time, but isn't the same function object). The call to removeEventListener does nothing if the given callback has not been added as an event listener for the given event, which is the case here.

To solve your problem, you need to store a reference to the closure that you pass to the addEventListener function. This way, you can get a reference to the same closure that was added when you need to remove it later in clearFileUploadListeners.

You can try something along the lines of the following code (untested):

import flash.utils.Dictionary;

var callbackRegistry:* = new Dictionary();


private function upload(file:FileReference, index:String):void {
    var dispatchEvent:Function = generateFileUploadCompleteCallback();

    callbackRegistry[file] = dispatchEvent;

    file.addEventListener(Event.COMPLETE, dispatchEvent);
}

private function clearFileUploadListeners(file:FileReference, index:String):void {
    var dispatchEvent:Function = callbackRegistry[file];
    callbackRegistry[file] = null;

    file.removeEventListener(Event.COMPLETE, dispatchEvent);

    var bool:Boolean = file.hasEventListener(Event.COMPLETE);
    if (bool)
        trace("ERROR");
    else
        trace("YAY, ALL OK!");
}

private function generateFileUploadCompleteCallback(index:String):Function {
    return function(event:Event):void {
        dispatch(event.type, event, index);
    };
}
Cameron
Thanks a lot, super clear explanation, I understand. I like your solution, it's very close to the one I had in mind (I defined a removeListeners() function inside my upload function, and stored it to an object). IE: object.var = removeListeners; then object.var(); It's similar to yours, I don't have to bother keeping track of a Dictionary but rather use the stack. Thanks for your help!
Rudy
Glad to have helped :-)
Cameron