as3 addEventListener - pass variable in function? - actionscript-3

Is it possible to pass a var at the end of an addEventListener?
/// clickType declared elsewhere in code.
checkBoxFast.addEventListener(clickType, goFast("yes"));
function goFast(evt:Event=null,myVar:String)
{
trace(myVar);
}

I guess if you want to parametrize your event handing I would suggest passing variables to the Event.
-Create a custom event:
public class MyEvent extends Event {
public var myVar:String;
public function MyEventHistoryEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false) {
super(type, bubbles, cancelable);
}
}
-Dispatch this event from the event dispatcher with the required variable:
var event:MyEvent = new MyEvent("eventType");
event.myVar = "yes";
dispatchEvent(event);
-Add an event handler:
checkBoxFast.addEventListener("eventType", eventHandler);
protected function eventHandler(event:MyEvent):void {
trace(event.myVar);
}

Another solution would be to use an anonymous function like so:
checkBoxFast.addEventListener(clickType, function(e:Event):void{goFast("yes")});
function goFast(myVar:String)
{
trace(myVar);
}

Creating custom event is best way I guess. But I was using sometimes different aproach. I dont know if it is good practice but it works in some cases.
public function test() {
var myVar : String = "some value";
addEventListener(MouseEvent.CLICK, onClick);
function(e:Event){
trace(myVar);
}
}

Here's a pretty clean way:
checkBoxFast.addEventListener(clickType, goFast("yes"));
function goFast(myVar:String) {return function(e:Event) {
trace(myVar);
}}
BUT beware anonymous functions, they won't let you end the listener in the same place it was made! If you keep repeating it like that many times in your application, it may get slow and freeze.
Actually, I really recommend you to do it like this:
var functionGoFast:Function = goFast("yes");
checkBoxFast.addEventListener(clickType, functionGoFast);
function goFast(myVar:String):Function {
return function(evt:Event = null):void {
trace(myVar);
}
}
//checkBoxFast.removeEventListener(clickType, functionGoFast);
See this answer for more examples and explanations on your case.

Related

allow event listener access to variable outside of its function

public class Main extends MovieClip {
public function Main():void {
getOne()
}
function getOne():void {
var varOne:Boolean = false;
stage.addEventListener(MouseEvent.CLICK, dunClicked);
}
function dunClicked(e:Event):void {
if(!varOne) trace("Nope...");
}
}
Now... I know if I put varOne within the Class it will be treated as a sort of 'global variable' (please correct me if I'm wrong...) I was wondering if there's another way for the EventListener to see the value of varOne?
Declaring 'varOne' as a member of the Main class is the best thing to do. Creating a closure (inner function) definitely works too, but not a very good practice because you can't remove an event listener defined using such an anonymous function.
It's not going to be "global", but a property of this class instance, which you should technically access by writing 'this.varOne' (and 'this.getOne()', 'this.dunClicked'...) but in AS3/Java/etc. you can just write 'varOne'.
public class Main extends MovieClip {
private var varOne:Boolean; // defaults to false;
public function Main():void {
getOne()
}
function getOne():void {
stage.addEventListener(MouseEvent.CLICK, dunClicked);
}
function dunClicked(e:Event):void {
if (!varOne) trace("Nope...");
// if (!this.varOne) trace("Nope..."); // this is equivalent
}
}
You can put the listener inside the function, so it will become a closure and will have access to local functions vars.
function getOne():void {
var varOne:Boolean = false;
stage.addEventListener(MouseEvent.CLICK, dunClicked);
function dunClicked(e:Event):void {
if(!varOne) trace("Nope...");
}
}

Overriding function from another class

I am defining this function in one of my classes:
public function onUse():void {};
Then in another of my classes (let's call it "class2"), I create a object of this class, and then want to override this function with another one. After some Google-Fu, I have found this, and used it...
button.onUse {
variable = value;
}
...but it executes instantly, and not when onUse() is called - which seems to be an empty function, always.
I didn't find anything more than that - I tried a few things myself, like specifying a function inside class2 and using button.onUse = function();, but it always throws errors.
Can anyone tell me whether what I am trying to do is actually possible, and if it is, how can I do it?
You can only override functions when you are extending the class:
public class A {
public function foo():void {
doStuff();
}
}
public class B extends A {
override public function foo():void {
doOtherStuff();
}
}
var n:A = new A();
n.foo(); // => calls doStuff();
var o:B = new B();
o.foo(); // => calls doOtherStuff();
Hence, assigning a different function to a class method of an instance is not possible at runtime.
You can, however, let your original class contain a field of type Function, and then simply assign a different closure to it.
public class A {
public var foo:Function;
}
var n:A = new A();
n.foo = function ():void {
doStuff();
};
n.foo(); // => calls doStuff();
var o:A = new A();
o.foo = function ():void {
doOtherStuff();
}
o.foo(); // => calls doOtherStuff();
check the syntax of
button.onUse {
variable = value;
}
a function would be defined as
public function onUse():void {};
and overwritten with
override public function onUse():void {
}
in a different class
the way you're trying to do it, does not constitute overriding a function.
What I've done in similar circumstances is create a onClickFunction function in the class
public var onClickFunction:Function = null;
and then in the CLICK event listener function add
if(onClickFunction != null){
onClickFunction();
}
then you can assign your on-click functionality by doing something like this
button.onClickFunction = function():void{
variable = value;
// dostuff
}
this is not the best way of doing it, but probably the easiest way of implementing the functionality. And ideally you'd use inheritance the way the spacepirate suggested.

How to pass arguments into event listener function in flex/actionscript?

Since when using sql lite if you try and do a function at the same moment it throws an error, im just trying to make a function that will check if its executing, and if it is try again in 10 milliseconds, this exact function works fine if i dont have to pass any arguments to the function but im confused how I can pass the vars back into the function it'll be executing.
I want to do:
timer.addEventListener(TimerEvent.TIMER, saveChat(username, chatBoxText));
But it will only allow me to do:
timer.addEventListener(TimerEvent.TIMER, saveChat);
It gives me this compile error:
1067: Implicit coercion of a value of
type void to an unrelated type
Function
How can I get this to pass this limitation?
Here's what I've got:
public function saveChat(username:String, chatBoxText:String, e:TimerEvent=null):void
{
var timer:Timer = new Timer(10, 1);
timer.addEventListener(TimerEvent.TIMER, saveChat);
if(!saveChatSql.executing)
{
saveChatSql.text = "UPDATE active_chats SET convo = '"+chatBoxText+"' WHERE username = '"+username+"';";
saveChatSql.execute();
}
else timer.start();
}
A function called by a listener can only have one argument, which is the event triggering it.
listener:Function — The listener function that processes the event.
This function must accept an Event
object as its only parameter and must
return nothing, as this example
shows:
function(evt:Event):void
Source
You can get around this by having the function called by the event call another function with the required arguments:
timer.addEventListener(TimerEvent.TIMER, _saveChat);
function _saveChat(e:TimerEvent):void
{
saveChat(arg, arg, arg);
}
function saveChat(arg1:type, arg2:type, arg3:type):void
{
// Your logic.
}
Another thing you can do create a custom event class that extends flash.events.Event and create properties that you need within.
package
{
import flash.events.Event;
public class CustomEvent extends Event
{
// Your custom event 'types'.
public static const SAVE_CHAT:String = "saveChat";
// Your custom properties.
public var username:String;
public var chatBoxText:String;
// Constructor.
public function CustomEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false):void
{
super(type, bubbles, cancelable);
}
}
}
Then you can dispatch this with properties defined:
timer.addEventListener(TimerEvent.TIMER, _saveChat);
function _saveChat(e:TimerEvent):void
{
var evt:CustomEvent = new CustomEvent(CustomEvent.SAVE_CHAT);
evt.username = "Marty";
evt.chatBoxText = "Custom events are easy.";
dispatchEvent(evt);
}
And listen for it:
addEventListener(CustomEvent.SAVE_CHAT, saveChat);
function saveChat(e:CustomEvent):void
{
trace(e.username + ": " + e.chatBoxText);
// Output: Marty: Custom events are easy.
}
Actually, you can pass additional parameters to an event listener without creating a custom event by using Actionscript's dynamic function construction.
private function addArguments(method:Function, additionalArguments:Array):Function
{
return function(event:Event):void {method.apply(null, [event].concat(additionalArguments));}
}
When setting up the closeHandler for the Alert window we call the addArguments() method and pass in an array continaing all of the parameters we want to pass to the closeHandler. The addHandler() method will return the function we will call when the Alert window closes with the parameters included.
protected function btnOK_clickHandler(event:MouseEvent):void
{
Alert.show("Would you like to reverse the text you just entered?", "", Alert.YES | Alert.NO, null, addArguments(alertCloseHandler, [txtInput.text]), null, Alert.YES);
txtInput.text = "";
}
1067: Implicit coercion of a value of type void to an unrelated type Function
Pay due attention to the error you got: it says that a Function is a type and that addEventListener() wants it. Although your listener returns void, it is a Function! So, what about returning the listener?
timer.addEventListener(TimerEvent.TIMER, saveChat(username, chatBoxText));
function saveChat(username:String, chatBoxText:String):Function {
return function(e:TimerEvent):void {
// Do everything that uses "e", "chatBoxText" and "username" here!
};
}
Simple like this. It works for any kind of event. And no closure issues.
Note on removeEventListener():
Don't try to do timer.removeEventListener(TimerEvent.TIMER, saveChat(username, chatBoxText)). It won't recognize your function because you'll be passing a new one on it every time you use saveChat(). Instead, just take its reference out into a variable and you're done:
var functionSaveChat:Function = saveChat(username, chatBoxText);
timer.addEventListener(TimerEvent.TIMER, functionSaveChat);
//trace(timer.hasEventListener(TimerEvent.TIMER));
timer.removeEventListener(TimerEvent.TIMER, functionSaveChat);
//trace(timer.hasEventListener(TimerEvent.TIMER));
You can try this:
var timerHandler:Function = function (event:TimerEvent):void
{
saveChat(username,chatBoxText,event);
}
timer.addEventListener(TimerEvent.TIMER, timerHandler);
Above calling saveChat(arg, arg, arg) it's not for me, i need to pass arguments that i dont have in this method but i have another solution
Im always using additional method passParameters to adding new arguments:
public function passParameters(method:Function,additionalArguments:Array):Function
{return function(event:Event):void{
method.apply(null, [event].concat(additionalArguments));}
}
explanation of this is here - its simple and always work
http://sinfinity.pl/blog/2012/03/28/adding-parameters-to-event-listener-in-flex-air-as3/

AS3 - Event listener that only fires once

I'm looking for a way to add an EventListener which will automatically removes itself after the first time it fires, but I can't figure a way of doing this the way I want to.
I found this function (here) :
public class EventUtil
{
public static function addOnceEventListener(dispatcher:IEventDispatcher,eventType:String,listener:Function):void
{
var f:Function = function(e:Event):void
{
dispatcher.removeEventListener(eventType,f);
listener(e);
}
dispatcher.addEventListener(eventType,f);
}
}
But instead of having to write :
EventUtil.addOnceEventListener( dispatcher, eventType, listener );
I would like to use it the usual way :
dispatcher.addOnceEventListener( eventType, listener );
Has anybody got an idea of how this could be done?
Any help would be greatly apprecitated.
(I know that Robert Penner's Signals can do this, but I can't use them since it would mean a lot of code rewriting that I can't afford for my current project)
I find the cleanest way without using statics or messing up your code with noise is to defining a global function (in a file called removeListenerWhenFired.as) like so:
package your.package
{
import flash.events.Event;
import flash.events.IEventDispatcher;
public function removeListenerWhenFired(callback:Function, useCapture:Boolean = false):Function
{
return function (event:Event):void
{
var eventDispatcher:IEventDispatcher = IEventDispatcher(event.target)
eventDispatcher.removeEventListener(event.type, arguments.callee, useCapture)
callback(event)
}
}
}
Then you can listen for events like so:
import your.package.removeListenerWhenFired
// ... class definition
sprite.addEventListener(MouseEvent.CLICKED,
removeListenerWhenFired(
function (event:MouseEvent):void {
... do something
}
)
)
I've not tried it, but you could just turn the EventUtil static method into a standard method and extend the class in your object.
public class OnceEventDispatcher
{
public function addOnceEventListener(eventType:String,listener:Function):void
{
var f:Function = function(e:Event):void
{
this.removeEventListener(eventType,f);
listener(e);
}
this.addEventListener(eventType,f);
}
}
public class Example extends OnceEventDispatcher
{
}
var ex:Example = new Example();
ex.addOnceEventListener(type, func);
functionadd.addEventListener(COMPLETE,functionremove);
functionremove()
{
runevent();
functionadd.removeEventListener(COMPLETE,functionremove);
}
function runevent()
{
trace('Hello');
}

Creating a custom trace() class in AS3

I got this idea of expanding my trace() messages.
Why
trace() is all over my code, I want to turn them on/off by a simple command and maybe add some sort of priority functionality to the trace(), i.e.
myTrace.TraceMsg("loosehere",debugme, 0);
myTrace.TraceMsg("winhere",debugme, 1);
And when I run, only the one with the higher priority, "1" in this case, shows.
There is a lot more functionality I would like to add as well, like logging messages to file and so on.
Problem
How do trace() work? -Is it possible to overload trace() somehow? -How would I implement the custom TraceMsg(what code here?) method?
Having some serious problems finding info on this subject on our favourite search engine, so any help would be appreciated.
I have come up with a rather efficient, yet tedious way of using my own trace() function in Flash only projects, but calling it simply with
trace("this", "that", "and that too");
I basically implement one trace() method in every class of my project, that calls a public function (so that i can call the real trace() function from there.
here is what I do : in every class I call this
include "trace_implementation.as";
in the .as file comes a simple method implementation (it could be a static method too).
public function trace(... arguments){
for(var i in arguments){
myTrace(arguments[i]);
}
}
and the myTrace function is defined in its own myTrace.as file
package pt.utils{
import flash.external.ExternalInterface
public function myTrace(_s:String):void{
trace(_s);// this will call the original flash trace() function
ExternalInterface.call("console.log", _s);// to get traces outside of flash IDE
/*implement what you want here*/
}
}
so now when I compile with "omit trace actions", my whole debugging is ignored as if I used trace() simply.
the really good part here is that you could implement custom actions depending on instructions you give in the trace, so :
trace(Debug.DEBUG_MESSAGE, "message to output in debug");
trace(Profile.START_PROFILING, this, 'name');
/*do heavy code*/
trace(Profile.STOP_PROFILING, this);
then dispatch it from myTrace, or a Tracer class or anything :)
Hope this helps future tracers.
trace() itself is a top-level function, not a class, so unfortunately we cannot extend it. That being said, we can utilize it in a simple class to do just what it does normally, only in this case the trace is based on conditions (i.e. Boolean - true|false, etc). First we create the Trace class, which we wouldn't instantiate ourselves because we are utilizing a Factory design pattern through the class below, Tracer. Tracer is built around the singleton design pattern, yet utilizes the Factory pattern to instantiate instances of Trace, when the trace method of Tracer is called.
//This class is handled by Tracer, which is right below it.
//You WILL NOT instantiate these, nor hold references.
package
{
public class Trace
{
private function _value:*;
private function _trace:Boolean;
public function Trace(pValue:*, pTrace:Boolean):void
{
_value = pValue;
_trace = pTrace;
}
public function get value():*
{
return _value;
}
public function get trace():Boolean
{
return _trace;
}
}
}
//This is the important class and the only one you will work with.
package
{
/**
*Utilizes Singleton and Factory design patterns.
*/
public class Tracer
{
private var _traceArray:Array;
private static var _instance:Tracer;
public function Tracer(pvt:PrivateClass = null):void
{
if(pvt == null)
{
throw(new Error("You cannot instantiate this class directly, please use the static getInstance method."));
}
_init();
}
public static function getInstance():Tracer
{
if(Tracer._instance == null)
{
Tracer._instance = new Tracer(new PrivateClass());
}
return Tracer._instance;
}
public function trace(pValue:*, pTrace:Boolean):void
{
var trace:Trace = new Trace(pValue, pTrace);
if(trace.pTrace)
{
trace(pValue);
}
}
//Since we have the option for individual traces to be disabled
//I provide this to get access to any and all later.
public function traceAll():void
{
traceStr:String = _traceArray.toString();
}
public function get traceables():Array
{
return _traceArray;
}
//Here we provide a method to trace all, even if set to false in their constructor.
private function _init():void
{
_traceArray = new Array();
}
}
}
//Here we create a class that is OUTSIDE of the package.
//It can only be accessed from within this class file. We use this
//to make sure this class isn't instantiated directly.
class PrivateClass
{
function PrivateClass():void
{
trace('can only be accessed from within this class file');
}
}
//Now for use in doc class
package
{
import flash.display.Sprite;
import flash.events.Event;
//No need to import Tracer and Trace, they are also in the
//unnamed package.
public class DocumentClass extends Sprite
{
private var _tracer:Tracer;
public function DocumentClass():void
{
if(stage) _init();
else addEventListener(Event.ADDED_TO_STAGE, _init);
}
private function _init(e:Event = null):void
{
_tracer = Tracer.getInstance();
_tracer.trace(10*20, false);
_tracer.trace(10*20, 0); //SAME AS ABOVE
_tracer.trace("I love AS3", true); //traces
_tracer.traceAll(); //Would trace: 200, 200, I love AS3
}
}
}
Keep in mind this is off the hip and very well could have a bug or two, but the idea is there; That is to say that this is not tested, it is merely to give you an idea of how you might implement this.
I hope this helps.
Look at the Flex logging API, particularly the section: Implementing a custom logger with the logging API.
Look up the TraceTarget class as well.
You can't override trace itself, but for ease of typing I like to create a global function called 'tr'. It's a little known fact that you can create global functions in AS3, but it's easy.
Create a file called tr.as inside you main source directory (not in a subdirectory or package), with the contents:
package {
public function tr(msg:String, ...):void {
// add custom trace logic here
trace("tr message: "+msg);
}
}
If you need to have a lot of logic or static storage variables etc, it might be better to make a separate static class, and have the global tr function call out to that, such as:
package {
import org.code.MyTracer;
public function tr(msg:String, ...):void {
MyTracer.tr(msg); // all the tracing logic is inside the MyTracer class
}
}
Here is a super simple custom trace function I use. debugFlag can be set to true/false
elsewhere in the package.
public static function myTrace(... vars) :void {
if (debugFlag) {
var output:Array = new Array;
for each (var arg in vars) {
output.push(arg);
}
trace(output);
}
}
In AS2, it was possible to override the global trace function by doing something like this (taken from memory, might be a bit wrong but the gist of it is there):
public static var realTrace:Function = _global["trace"];
// This is put in some init code somewhere
_global["trace"] = myTrace;
public static function myTrace(... args):void
{
// Do whatever you want with args here, build a nice formatted string or whatever
// before passing to realTrace. Using with MTASC one could add line numbers, class
// names and all sorts of nice meta data. Or just return should you want to turn
// tracing off.
realTrace.apply(args);
}
Unfortunately I haven't found a way to do the same in AS3. Yet.
Trace is a top-level function, so you can't override it, and as far as I know, it does not fire any events. Since it's a top-level function (not contained in any named package), you can use it without import statements.
Here is an example of a top-level "Tracer" class that you can use in place of trace without import statements.
Just call "Tracer.write" or "Tracer.writeError" for tracing Error objects.
"Tracer.write" accepts a variable number of arguments, just like the built-in trace function. "Tracer.writeError" is a helper method that allows you to easily trace Error objects.
Features:
Calls built-in trace.
Keeps a log of all your calls to Tracer.write as an array of strings.
The call log is accessible as a string through getText, which joins all elements in the array with a newline character and will optionally tack on line numbers!
Fires events when new lines are added to the log, so if you have some kind of display window for the log, the display window can listen for Tracer events to update the log display in real-time as the events occur. This is great for displaying trace events when running inside a web browser or stand-alone player.
-Tracer class definition
package
{
import flash.events.EventDispatcher;
public class Tracer extends EventDispatcher
{
private static var traced_text:Array = new Array( "--Start of Trace Log--" );
public static var enabled:Boolean = true;
private static var suspended:Boolean = false;
public static var instance:Tracer = new Tracer();
public static const newline:String = "\n"; //workaround for TextField.appendText bug.. use "\n" instead of "\r". See note and link to bug post in getText method
public function Tracer()
{
}
static public function write( ...args ):void
{
if (enabled && !suspended)
{
trace.apply( null, args );
var text:String = args.join( newline );
var next_index:int = traced_text.length;
traced_text.push( text );
suspended = true; //prevent recursive calls from TracerEvent handler
instance.dispatchEvent( new TracerEvent( text, next_index ) );
suspended = false;
}
}
static public function writeError( e:Error ):void
{
write( "errorID: " + e.errorID, "errorName: " + e.name, "errorMessage: " + e.message, "stackTrace: " + e.getStackTrace() );
}
static public function getText( include_line_numbers:Boolean ):String
{
var line_count:int = traced_text.length;
var lines:Array = traced_text; //store pointer to traced_text; pointer may be changed to reference an altered array that includes line numbers
if (include_line_numbers) //create temporary trace log copy with altered lines; allows quick call to join at end
{
var new_lines:Array = new Array();
for (var i:int = 0; i < line_count; i++)
new_lines.push( i.toString() + ": " + lines[i] );
lines = new_lines;
}
return lines.join( newline ); //do not include last newline character (workaround for bug in appendText method (https://bugs.adobe.com/jira/browse/FP-1982); I have to call appendText with newline character first, otherwise it has issues like not acknoledging the newline thats already there at the end).
}
static public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
{
instance.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
static public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
{
instance.removeEventListener(type, listener, useCapture);
}
static public function willTrigger(type:String):Boolean
{
return instance.willTrigger(type);
}
static public function hasEventListener(type:String):Boolean
{
return instance.hasEventListener(type);
}
}
}
-TracerEvent class definition
package
{
import flash.events.Event;
public class TracerEvent extends Event
{
public static const WRITE:String = "te_write";
public var text:String;
public var index:int; //index of newly traced text in the traced_text array (trace log)
public function TracerEvent( text:String, index:int )
{
super( WRITE, false, false );
this.text = text;
this.index = index;
}
override public function clone():Event
{
return new TracerEvent( text, index );
}
}
}
As mentioned below, there is no way to override trace (at least not if you want your traces to reach the output stream), but it's actually very easy to create your own universally accessable logging function. Plus, you can even define a universally accessable boolean to turn logging on or off:
log.as (note that the filename must reflect the name of the function)
package {
function log(... arguments):void {
trace("Custom logging FTW!");
if (logEnabled)
trace(arguments);
}
}
logEnabled.as (note that the filename must reflect the name of the variable)
package {
var logEnabled:Boolean = true;
}
Main.as
package {
import flash.display.MovieClip;
public class Main extends MovieClip {
public function Main() {
log("Testing");
logEnabled = false;
log("Testing2");
}
}
}
Response
Custom logging FTW!
Testing
Custom logging FTW!
you dont need to override it , just create a function in your project and call it trace then any trace call will point to this.trace ;)
function trace(... arguments){
yourfunction(arguments);
}