Pasar un parámetro al detector de eventos en AS3 de la manera más simple … ¿existe?

Esperado / pseudo ejemplo:

stage.addEventListener(MouseEvent.CLICK, onClick.someWayToPassParameters(true, 123, 4.56, "string")); function onClick(e:MouseEvent):void { trace("Received " + someWayToRetrieveParameters().b/i/n/s + "."); } 

Durante muchos años (3 ~ 4), en cada sitio web, foro, blog, dondequiera que haya buscado, la gente me dice que no hay una forma sencilla de hacerlo. Por lo general, sugieren a:

  • Agregue el oyente a un objeto dynamic, donde puede establecer el valor a una propiedad adicional y hacer referencia a él (e.target.property / e.currentTarget.property) en la función.

    No todas las clases son dinámicas. No funcionará en un Sprite, por ejemplo.

  • Extienda la clase del objeto con una clase personalizada para agregar propiedad o simplemente para que sea dinámica.

    Tendrás que crear una clase de modificación completamente nueva cada vez.

  • Use una función anónima como manejador de eventos.

    No hay referencia (y es fea). Para eliminar el oyente para liberar recursos, se lo fuerza a hacerlo desde el interior de la función con arguments.callee.

  • Llame a otra función, utilizando el parámetro, dentro del controlador de eventos.

    ¿Y dónde, en el evento, la llamada del manejador va el parámetro?

  • Mantenga el controlador de eventos en el mismo ámbito que el parámetro.

    Incumplimiento de un lío semántico total.

  • Encapsule la definición del controlador de eventos y la llamada addEventListener en una función que recibe el destino y los parámetros.

    Puede mezclar ámbitos, pero está cerca. Sin embargo, tienes que ser cuidadoso.

… Entre muchas otras soluciones sugeridas.

Todo lo que quiero es pasar una discusión al controlador de eventos para que pueda usarlo dentro de su función, ¡como cualquier función normal!

¿Estoy pidiendo demasiado?

Fuera de la caja: solo toma 2 líneas extra de código elegante para resolver este antiguo rompecabezas.

 stage.addEventListener(MouseEvent.CLICK, onClick(true, 123, 4.56, "string")); function onClick(b:Boolean, i:int, n:Number, s:String):Function { return function(e:MouseEvent):void { trace("Received " + b + ", " + i + ", " + n + " and " + s + "."); }; } 

Pero, lo más importante, es muy probable que deba eliminar el oyente más tarde para liberar recursos, por lo tanto, línea +1 para almacenarlo en una variable:

 var functionOnClick:Function = onClick(true, 123, 4.56, "string"); stage.addEventListener(MouseEvent.CLICK, functionOnClick); function onClick(b:Boolean, i:int, n:Number, s:String):Function { return function(e:MouseEvent):void { trace("Received " + b + ", " + i + ", " + n + " and " + s + "."); }; } 

Y podrás eliminarlo normalmente:

 trace("Before: " + stage.hasEventListener(MouseEvent.CLICK)); stage.removeEventListener(MouseEvent.CLICK, functionOnClick); trace("After: " + stage.hasEventListener(MouseEvent.CLICK)); 

Aquí hay un ejemplo más elaborado y dynamic para demostrar su uso:

 function onClick(s:String):Function { return function(e:MouseEvent):void { trace("The square " + s + " at x = " + e.currentTarget.x + "px was clicked"); }; } var myFunctions:Array = new Array(); for (var i:int = 0; i < 10; i++) { myFunctions.push(onClick("#" + (i+1))); } for (i = 0; i < myFunctions.length; i++) { var square:Sprite = new Sprite(); square.name = "sqr" + i; square.addChild(new Bitmap(new BitmapData(20, 20, false, 0))); square.x = 5 + 25 * i; square.addEventListener(MouseEvent.CLICK, myFunctions[i]); stage.addChild(square); } 

Sin propiedades a través de objetos dynamics, sin clases personalizadas, sin funciones sueltas, sin superposición de scope. Justo lo que la lógica espera que sea: simplemente le pasa argumentos.

Y para eliminar a cada oyente de forma adecuada, puede hacerlo así más tarde:

 for (i = 0; i < myFunctions.length; i++) { square = stage.getChildByName("sqr" + i) as Sprite; trace("Before (#" + (i+1) + "): " + square.hasEventListener(MouseEvent.CLICK)); square.removeEventListener(MouseEvent.CLICK, myFunctions[i]); trace("After (#" + (i+1) + "): " + square.hasEventListener(MouseEvent.CLICK)); stage.removeChild(square); } 

En mi humilde opinión, esta es la manera más simple pero más sólida de hacerlo.

Aquí hay un ejemplo

 var s1:Boolean = true; station1.addEventListener(MouseEvent.ROLL_OVER, function(e:MouseEvent) : void { spread(e, s1) }); station1.addEventListener(MouseEvent.ROLL_OUT, function(e:MouseEvent) : void { collapse(e, s1) }); function spread(event:MouseEvent ,bb:Boolean):void { if(bb != true) event.currentTarget.gotoAndPlay(2); } function collapse(event:MouseEvent,bb:Boolean ):void { if(bb != true) event.currentTarget.gotoAndPlay(18); } 

No es posible pasar un parámetro directamente al controlador de eventos.

La mejor aproximación es mediante el uso de un método de fábrica de funciones, IMO

// – EDITADO: Creé una clase separada para mantener controladores parametrizados, ver https://gist.github.com/4124722

el uso es simple:

  public class Usage extends Sprite{ private var _handlers : ParameterizedHandlerContainer; public function ParameterizedEvents() { _handlers = new ParameterizedHandlerContainer(); _handlers.registerHandler( this, Event.ADDED_TO_STAGE, someMethod, 3000 ); } private function someMethod( event : Event, amount : uint ):void { trace( this, 'someMethod', amount ); _handlers.destroyHandler( arguments.callee, event ); } } 

Ahora puede registrar manejadores parametrizados con ParameterizedHandlerContainer#registerHandler y destruirlos con ParameterizedHandlerContainer#destroyHandler .

Puede pasar cualquier cantidad de parámetros que desee, pero el primer parámetro del método de callback debe ser de tipo Evento o un descendiente.

Yo modificaría levemente el ejemplo de Creynders:

  protected function createHandler(handler:Function, ...args):Function { var h:Function = function(e:Event = null):void { trace(args); if (handler != null) handler(e); } return h; } sm.addEventListener(StyleEvent.STYLE_CHANGE, createHandler(updateStyle, 1,true,"Lorem",["a","b","c"]), false, 0, true); 

De esta forma puede usar manejadores de eventos originales e inyectarle los “argumentos”,

también podría hacer que sus manejadores tengan la siguiente firma:

 protected function myHandler(e:Event, ...args):void; 

entonces podría pasar argumentos directamente al controlador de destino:

  protected function createHandler(handler:Function, ...args):Function { var h:Function = function(e:Event = null):void { //trace(args); if (handler != null) { args.unshift(e); handler.apply(this, args); } } return h; } 

atentamente

¿Por qué no usar AS3-Signals? Pasar un parámetro es tan simple como:

 import org.osflash.signals.Signal; public class AlarmClock { public var alarm:Signal; public function AlarmClock() { alarm = new Signal(String); } public function ring():void { alarm.dispatch("9 AM"); } }