AS3: Best way to register event listeners - actionscript-3

My question today is about the way we can register an event listener.
Let's say we have a Group element and inside it a custom Handler element. We want the Handler element to do something when Group triggers a custom event. Now, what is the best way to do it?
var group:Group = new Group();
var handler:Handler = new Handler();
group.addElement(handler);
Now, what is the best way to register the event listener?
1. Go on and do it from the file where we initialized the objects
group.addEventListener("CustomEvent", handler.handlerFunction);
2. Register the event listener from the Handler's class:
parent.addEventListener("CustomEvent", handlerFunction);
3. Any other way?

You can let Group class instance dispatch custom event directly on Handler class instance. Handler class would have an internal listener registered for example in constructor.
public function Handler() {
addEventListener("CustomEvent", handlerFunction);
}
Group class would dispatch event following way:
handler.dispatchEvent(new CustomEvent());

Related

How to dispatch an event with dynamic content in Flex?

I often have the requirement to dispatch a flash.events.Event with soem custom String text, like:
protected function mouseClicked(event:Event) {
//here I'd want to notify anyone interested in the button click,
//and also transfer the name of the button (or whatever) that was clicked - assume some dynamic value
dispatchEvent(new Event("myMouseEvent"), button.name));
}
Of course the above event is invalid. But is there any event that can be used for that type of events? Maybe the TextEvent, but I don't know if I'd be misusing it here.
To include additional data with your events, create a custom event class by extending Event (or any sub-class of Event) and adding your own properties. For example:
class NameEvent extends Event {
public static const NAME_CLICK:String = "nameClick";
public var name:String;
public function NameEvent(type:String, name:String) {
this.name = name;
super(type);
}
}
dispatchEvent(new NameEvent(NameEvent.NAME_CLICK, button.name));
Note that your event type strings ("nameClick" in this example) should be globally unique, otherwise listeners could get them confused with other event types. For example "click" is already expected to be a MouseEvent. I often add prefixes to my custom event types, for example "NameEvent::click".
Another option that does not require creating a custom event is to rely on the expected target to get additional data. For example:
// dispatch a custom event from a Button
dispatchEvent(new Event("myClick"));
// handler for "myClick" events on the button
function myClicked(e:Event):void {
var button:Button = e.target as Button;
trace(button.name);
}
This is not as flexible and also more fragile than using a custom event class, but sometimes a quick easy solution.

Custom Event not being heard

I'm working on a mobile AIR project using Flash Builder 4.6. I'm dispatching a custom Event but it is never being heard. I've checked similar questions but cannot find an answer which solves it for me. In my main mxml file I add the listener in the addedToStage event:
[EDIT] adding the listener to the userStatus instance. Still not working.
<s:ViewNavigatorApplication
...
private var userStatus:UserStatus;
protected function addedToStageHandler(event:Event):void
{
userStatus = new UserStatus();
userStatus.addEventListener(CustomEvent.CREDENTIALS_READY, credentialsReadyHandler);
The CustomEvent class:
public class CustomEvent extends Event
{
public static const CREDENTIALS_READY:String = "credentialsReady";
public function CustomEvent(customEventString:String){
super(customEventString, true, false);
}
}
The class which dispatches the CustomEvent after a successful service call:
public class UserStatus extends EventDispatcher
{
...
//event handler
private function userStatusLoaded(e:Event):void
{
var json:Object = JSON.parse(paidUserLoader.data.toString());
if(json.success == "true")
{
trace("UserStatus::this is a paid user!");
dispatchEvent(new CustomEvent(CustomEvent.CREDENTIALS_READY));
}
I can see the trace statement in the console, so I know the code is getting there. But the listener in the s:ViewNavigatorApplication class never fires.
Can anyone see why?
Thanks.
Any custom class event has to override the clone() method. This method is automatically called internally but if it's not present (overrided) then only direct dispatch can work but events cannot traverse a hierarchy.
EDIT: nvm, there's no reason for your ViewNavigatorApplication to actually catch that event since UserStatus is not a DisplayObject. You are confused about how event are dispatched and are supposed to work. Only the UserStatus itself can catch the event it is dispatching. No other object can. In the case of a display list event propagate but for all non displayobject they need to catch their own dispatch.
EDIT: Answering comment:
There's somewhere a reference of the UserStatus instance, it might be in the ViewNavigatorApplication or not, if it is then:
myuserstatus.addEventListener(CustomEvent.CREDENTIALS_READY, credentialsReadyHandler);
would work. If it's not there then you need to pass along the information from whether class having the reference to UserStatus to the ViewNavigatorApplication. This can be done by redispatching the event (this is where you need to override clone()).
The instance of someotherclass has a reference to userstatus and does listen for the event. When the event fires the someotherclass instance catches and redispatches it. The ViewNavigatorApplication listen for that event directly or via the someotherclass instance reference and finally catches the event.
EDIT: responding to new edits
Let's fix your custom event class. By default you set bubbling to true but only DisplayObject can bubble so it should be set to capture (false)
super(customEventString, false, false);
That should fix it I bet.

Robotlegs - second dispatch does not work with addViewListener

I have a mediator created in Robotlegs, and its related view would dispatch two different kinds of events. When the mediator captures the event, it would just simply dispatch the event. The problem I ran into was that the first event is re-dispatched flawlessly, but the 2nd event was not dispatched.
However, if I manually assign a different handler to the 2nd event, the event is properly captured.
Below is the relevant code:
public class MyMediator extends Mediator
{
[Inject]
public var view:MyView;
public override function onRegister():void
{
super.onRegister();
addViewListener( SomeEventTypeA.COOL_EVENT, dispatch, SomeEventTypeA ); // This event is dispatched correctly
addViewListener( SomeEventTypeB.STUCK, dispatch, SomeEventTypeB ); // This one is not correctly dispatched
//A twist, if I uncomment the following code, the event is captured by its handler
//addViewListener( SomeEventTypeB.STUCK, view_stuck, SomeEventTypeB );
}
private function view_stuck( event:SomeEventTypeB ):void
{
//ah ha, this is called correctly if the above relevant line is uncommented
}
}
Found the cause:
The event needs to have a proper clone method in order to be re-dispatched correctly. See related link:
http://knowledge.robotlegs.org/kb/application-architecture/why-doesnt-my-event-trigger-the-command-it-is-mapped-to

Program Flow and Event Listeners in ActionScript

I'm having difficulty with program flow control when XML loads from an external source. I'm using Flash Builder 4.6
What should happen: use a loader. Listen for when it is loaded. When it fires the COMPLETE event, proceed.
What is happening: use a loader. Listen for when it is loaded. But before the COMPLETE event fires, program control returns to the calling class. So meanwhile, the program keeps going and throws an error because it is trying to access data which is null.
Three classes are involved:
AdvocacyWiz.mxml - the base MXML file for this AIR mobile app
Model.as - the model class
myXMLLoader.as - class in which the loader loads data, then
dispatches event when it's done.
I am calling a function setUpModel() in AdvocacyWiz.mxml once AdvocacyWiz.mxml is added to the stage. The setUpModel function in that class just looks like this:
Model.Instance.initialize(); //model is a singleton
The initialize function call invokes a loader in my XMLLoader.as class. That's where the event listener is left listening for the Complete event, which happens too late to prevent a null error from occurring.
EDIT: Here's the code --
In AdvocacyWiz.mxml (this fires first):
protected function addedToStageHandler(event:Event):void
{
setUpModel();
stage.scaleMode = StageScaleMode.NO_SCALE;
//... additional layout functions
private function setUpModel():void {
Model.Instance.initialize();
}
In Model (this fires second):
public function initialize():void {
addEventListeners(); //this includes listener for StoriesXMLLoader.STORY_LOADED event which will be dispatched from the loader class.
useExternalXML();
...
}
private function useExternalXML():void
{
myLoader.getStory("1140");
}
In the Loader class:
public function getStory(storyId:String):void {
var url:String = "http://mysite.com/whatever.xml";
myLoader.dataFormat = URLLoaderDataFormat.TEXT;
myLoader.addEventListener(Event.COMPLETE, storyXMLLoaded);
myLoader.load(new URLRequest(url));
}
private function storyXMLLoaded(e:Event):void {
storyXML = new XML(e.target.data);
dispatchEvent(new Event(StoriesXMLLoader.STORY_LOADED));
}
What I want to do is call setUpModel() but not have flow returned to the mxml class until the model actually has data.
Thanks.
Well, you haven't provided any code, which would help considerably, but here's a few pointers.
What should happen: use a loader. Listen for when it is loaded. When
it fires the COMPLETE event, proceed. What is happening: use a loader.
Listen for when it is loaded. But before the COMPLETE event fires,
program control returns to the calling class. So meanwhile, the
program keeps going and throws an error because it is trying to access
data which is null.
A lot of Flex is asynchronous. What this means is that when you try to load from the external source, program control immediately returns, as you noted.
In order to have the kind of program flow you want, you need to have the function that calls load() end after that call. Then, when the COMPLETE event listener fires, you can execute the remainder of the code that causes the null exception.

Add EventListener to function?

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.