Add EventListener to function? - actionscript-3

Quick question... Is is possible to attach an EventListener to a function? Such that if at any point in a function's execution an Event is Dispatched the EventHandler will get fired?
Cheers.

You can use the stage to dispatch such an event and listen to it:
stage.addEventListener("myFunctionWasCalled", callback);
myFunction();
public function callback(event:Event):void {
trace("callback executed");
}
public function myFunction():void {
stage.dispatchEvent(new Event("myFunctionWasCalled"));
}

Event listeners are attached to objects that belong to a class that descends from EventDispatcher. You cannot attach them to a function.

You can achieve this by simply passing a function reference (callback) as an argument
function cb(s:String):void {
trace(s);
}
function doit(f:Function):void {
// do something
f("Hi");
// do some more stuff
}
doit(cb);

If you only want to listen for a event in the lifetime of a function call just add/remove as needed.
example:
function somefunction():void{
someobject.addEventListener(Event, eventhandler);
... doing stuff
someobject.removeEventListener(Event, eventhandler);
}
Now keep in mind if your doing this then your choice in even flows may become very hard to track down the road.
Typicality you only really need to worry about this in the life and death of a object vs the life and death of a function call.

To attach an event listener to an object this object must implement IEventDispatcher interface. So you could extend Function class and add your methods. AS3 docs say that Function is not final, but AS3 compile disagrees with it: VerifyError: Error #1103: Class Bla cannot extend final base class.
So, the short answer is: you can't attach an event listener to a function.
But as it was already mentioned you could:
Pass callback/callbacks to the function which will be called at some point: function bla(callback1:Function, callback2:Function):void.
Dispatch events from some other object during function execution, for example you could make a Functor class which has method execute() and implements IEventDispatcher. In this way you'd call the function myFunctor.execute() and could get dispatched events.

Related

my_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, startListener); function startListener (e:Event):void;

can you please help me to find what does e: mean in actionscript 3.0 ?
my_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, startListener);
function startListener (e:Event):void;
Event listeners (your function startListener) must receive an Event object. Generally this is written as e:Event but could just as easily be hamSandwich:Event. The important part is that it is an Event object.
e is the Event that was dispatched.
You have an object that's called / referenced by my_loader.contentLoaderInfo. At some point, my_loader.contentLoaderInfo might create an Event object, categorize it as a "complete event" (in other words, Event.COMPLETE), and dispatch it to any functions listening to my_loader.contentLoaderInfo for an Event.COMPLETE event. This is done by calling the functions that are listening for that event.
In this case startListener is one of those functions that's listening for that event on my_loader.contentLoaderInfo, so whenever that kind of event is dispatched from that object, one of the functions that's called is startListener. Notice how the type of object that is dispatched and the type for startListener's lone parameter are the same: Event.
A function that's listening to an object for a type of event may wish to examine the event for certain pieces of information, so when that event is dispatched, it is copied by reference to the listening function as the lone argument. So e is a reference to the event that was created and dispatched.
This next little bit may be jumping ahead, but this is an example of how that might be used: One thing some people like to do is to make the same function listen to multiple objects for the same type of event. Something like:
obj1.addEventListener(Event.COMPLETE, myHandler);
obj2.addEventListener(Event.COMPLETE, myHandler);
obj3.addEventListener(Event.COMPLETE, myHandler);
function myHandler(e:Event):void {
// do some stuff
}
So what if one of those objects dispatches a complete event? How does myHandler know which object it belongs to? By looking at e.target:
function myHandler(e:Event):void {
this.doSomething(e.target);
}
e.target is the actual object that dispatched the event, so if the function is listening to multiple objects for the same type of event, e.target would let the function tell those objects apart.
It's a parameter you passes in your custom function e.g.
//Here, data type is a Number and var name is myNum
function doSquare(myNum:Number):void
{
var mySquare:Number = myNum * myNum;
}
Similarly,
//here type is Event and var name is e (you can have any name like evt, event, myEvt etc)
function startListener (e:Event):void
{
trace(e.target.content.width);
trace(e.target.width);
trace(my_loader.width);
}

Can dispatchEvent() carry arguments in AS3?

Behold this example:
addEventListener("myEventType", myFunction("argument"));
function myFunction(args:String):Function {
return function(evt:Event):void {
trace(evt.currentTarget, "has", args);
};
}
dispatchEvent(new Event("myEventType", true));
It works.
Can I do something similar, but passing "argument" through dispatchEvent()?
It'd be very handy in a situation where dispatchEvent() is in a wholly separated class from addEventListener() and myFunction().
I'll be needing this a lot, so I want to do it without creating a custom event class for every situation.
You can use native flash.events.DataEvent for passing String parameter or create custom DataEvent with data:* property in all situations where you need to pass parameters to event handler.
If you want to customize the behavior of event listener in the place of adding event listener you can create "listener" object for holding this custom parameters (but I think this technique is more complicated than custom events):
addEventListener("myEventType", new EventListener("param1").onEvent);, whereEventListener is the class like this:
public class EventListener
{
private var params:*;
public function EventListener(params:*)
{
this.params = params;
}
public function onEvent(event:Event):void
{
trace("onEvent, params = ", params);
}
}
You could take a look at Signals (https://github.com/robertpenner/as3-signals). They are an alternative to Events and you can send whatever extra params you want with a Signal.

How to access the variable "abc" in the following code structure

1) First of all I don't wanna use CustomEvent class. Some solution I am looking without using CustomEvent.
2) One of the solution can be having abc variable in ClassA. And then dispatching directly via ClassA ( rathar than saying classB.dispatchEvent() ). But still looking if there is some better solution than this.
//Frame1 code :
import flash.events.Event;
var classA:ClassA = new ClassA() ;
classA.addEventListener("hello", hello);
classA.init();
function hello(e:Event)
{
trace(e.currentTarget.abc); //<<<< NEVER EXECUTED
}
//classA
package
{
import flash.display.MovieClip;
import flash.events.Event;
public class ClassA extends MovieClip
{
var classB:ClassB ;
public function ClassA()
{
classB = new ClassB();
}
public function init()
{
classB.dispatchEvent( new Event("hello"));
}
}
}
//classB
package
{
import flash.display.MovieClip;
public class ClassB extends MovieClip
{
public var abc:Number =123;
public function ClassB()
{
}
}
}
You are missing a couple key concepts before you can get your example to work. First you are dispatching the event on an instance of ClassB, however you are listening on an instance of ClassA. So, they have to be related in some way, in order for event to be properly orchestrated when it gets dispatched. One way to do that is to use event bubbling. One caveat to that is that native event bubbling only really works for DisplayObjects, but both of your classes inherit from MovieClip so thats not a big deal.
So the first thing, you have to understand how bubbling events work. A simplified explanation is that events start at the top of the display hierarchy and capture down the display tree towards the element, they are finally dispatched on the target, then they turn around and bubble back out in the opposite direction.
This means that your instance of ClassB has to be a child of ClassA. So the first thing you'll have to change is in your ClassA constructor:
public function ClassA()
{
classB = new ClassB();
addChild(classB);
}
Next, when you dispatch the event, you'll need to explictly say that its a bubbling event, otherwise it'll be triggered on the target, and neither capture nor bubble through the display stack.
public function init()
{
classB.dispatchEvent( new Event("hello", true));
}
The second argument of true sets the event to a bubbling event.
Finally you'll need to change your handler. Right now, it's using e.currentTarget, which isn't going to be what you expect in this case (usually it is, thought).
You have to understand the difference between e.target and e.currentTarget. e.target is the actual target of the event, independent of how its bubbling or capturing. e.currentTarget on the other hand is the element which is presently handling the event. So in your case e.currentTarget is an instance of ClassA (the instance that the event handler is actually attached to) and e.target is an instance of ClassB (the instance that the event was dispatched on). During the event lifecycle, e.currentTarget will change as the event moves around, but e.target should always be the same.
In this case, you want to reference the actual target of the event, not the element that is currently processing the event. So you need to change your handler to:
function hello(e:Event)
{
trace(e.target.abc);
}
And then it should work. You can find a working example here that encapsulates the changes I've described.
If these classes weren't DisplayObjects then you would have to take a different approach -- either by using a signal pattern or to manually listen for an retrigger the event inside ClassA.
First of all you are adding an event listener to classA but your classA init method is asking classB to dispatch an event and this is the reason why your code does not get executed. If you want to catch the hello event you should be doing something like
public function init()
{
this.dispatchEvent( new Event("hello"));
}
Or you should be registering the listener on classB (which is not in scope so no code suggestion).
In ActionScript the best approach to transfer information is to use custom events so my suggestion is to re evaluate your decision on custom events.

Creating your own ADDED_TO_STAGE event

Is it possible to create your own ADDED_TO_STAGE event?
I´m trying to pass some arguments to its handler...
It would be like this:
addEventListener(Event.ADDED_TO_STAGE, arg1, arg2, init)
There´s any workaround for this?
Thanks.
Visiting this link will provide an in-depth answer on this, however here's a quick and dirty snapshot:
A function called by a listener can only have one argument, which is the event triggering it.
You will need to either call another function from your listener function, or create a custom event to hold the properties you want to parse. The latter is recommended, but here's how you could implement the former:
function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
finalize(arg1, arg2);
}
function finalize(a:*, b:*):void
{
trace(a, b);
}

adding a custom event listener in as3

I've done a lot of reading through forum posts and tutorials, but I still can't wrap my brain round events and event listeners. I have a pretty simple example, but I can't get it to work.
I have an arrayCollection of custom objects in a repeater, when one of those objects is clicked, I want a different componenet to display data associated with that object.
Here's what I have, but the listener never responds (the dispatcher seems to be working though, because the new event is created and I can see the trace with the proper output.) I suspect it is because when I call addEvent Listener, I am doing so on the wrong object. My understanding is that the object that will display the object data is the object that should have the event listener, and listen for all events of this nature, but maybe I misunderstood.
My custom event:
public class SelectObjectEvent extends Event
{
public function SelectObjectEvent(customEventString:String, myObject:customObject)
{
super(customEventString, true, false);
trace(customEventString+" "+myObject);
}
}
}
My custom object has the following function which is called on click:
public function selectObject(myObject:customObject):void
{
dispatchEvent(new SelectObjectEvent("OBJECT_SELECTED", customObject));
}
And the component I want to display the selected object has the following constructor:
public function SelectedObjectDisplayClass()
{
addEventListener("OBJECT_SELECTED", this.showObject)
}
public function showObject(event:Event):void
{
trace("Show object: "+event);
}
It's not quite clear where your last two code chunks are, but it looks like you need to be calling addEventListener on the object that extends EventDispatcher.
That is, if your second chunk belongs to a custom object called Clickable, which extends EventDispatcher and calls dispatchEvent() when clicked, then your component should be calling myClickable.addEventListener(...) where myClickable is an instance of Clickable. Does that make sense?
But assuming your 3rd code chunk is not in the same class as the second, it doesn't look like you're doing that. You're adding a listener to the class that owns the third chunk of code, which I gather is not the one that extends EventDispatcher.
Just a quick glance at your code and notice that your dispatchEvent second parameter is the class not the instance of the object. Shouldn't this be myObject?
public function selectObject(myObject:customObject):void
{
dispatchEvent(new SelectObjectEvent("OBJECT_SELECTED", **customObject**));
}