"Warning: 1090: Migration issue" despite explicitly registering event handlers - actionscript-3

I have a game engine class that inherits from MovieClip and handles mouseDown events in a private instance method. For the sake of simplicity, I name the event handler onMouseDown. It looks like this:
private function onMouseDown(e:MouseEvent):void
{
if (_isEnginePlaying)
{
_player.attack();
}
}
I register it in the engine class's init method (itself an addedToStage handler) that looks like this:
private function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// ...
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
// ...
}
This compiles and works correctly, but the compiler warns:
Warning: 1090: Migration issue: The onMouseDown event handler is not triggered automatically by Flash Player at run time in ActionScript 3.0. You must first register this handler for the event using addEventListener ( 'mouseDown', callback_handler).
But, as shown, I did register the handler using addEventListener(). Why does the compiler still emit this warning and what can I do to make the warning go away?

This is because the handler was registered with a different instance and not the game engine instance (this). Remember that event handlers in AS2 were registered simply by specifying them as properties on the instances that should handle the respective events and will automatically fire when needed. The warning is there to inform the developer that they do not automatically fire in AS3.
So the compiler errs on the side of caution, assumes I'm trying to register a click handler with the game engine instance (even though I've already registered it with the stage) and warns me to that effect.
There are a number of ways to make the warning go away:
Just rename the onMouseDown handler. The "on-" convention is a holdover from AS2 and the compiler only emits warnings for handlers following this naming convention; if you're comfortable settling for a different convention, this is the recommended solution. The convention used at Adobe is "-Handler" (sources 1 2 3), so onMouseDown becomes mouseDownHandler:
private function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// ...
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
// ...
}
private function mouseDownHandler(e:MouseEvent):void
{
if (_isEnginePlaying)
{
_player.attack();
}
}
If this can listen for the event, register the event with this instead:
private function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// ...
addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
// ...
}
If the intention was for this to listen for the event, then registering the handler with a different instance was inappropriate.
If you need to register the handler with a different instance, but you're sure that the definition is where it should be, you'll have to rename the handler. If for whatever reason changing the naming convention across all of your event handlers is not an option, the least you should do is prefix the name of whatever you're registering it with, so it's clear that this handler is not intended to be registered with this, but with a different instance:
private function stage_onMouseDown(e:MouseEvent):void
{
if (_isEnginePlaying)
{
_player.attack();
}
}
If you insist on keeping things the way they are, you can suppress the warning altogether, but obviously this is not something you should be doing unless you have a very good reason to.

Related

is it ok to call removeEventListener() twice?

private function saveDatabase(obj:Object):void{
database.setValue(obj);
database.addEventListener( DatabaseReferenceEvent.VALUE_CHANGED, dbValueChanged );
}
private function dbValueChanged(event:DatabaseReferenceEvent):void {
database.removeEventListener( DatabaseReferenceEvent.VALUE_CHANGED, dbValueChanged );
//do something here
}
private function destroy():void{
database.removeEventListener( DatabaseReferenceEvent.VALUE_CHANGED, dbValueChanged );
// remove all other objects
}
In the above code sometimes due to latency issues, the value changed event may not get triggered. So while calling destroy() I am not sure if the event listener is removed or not? Is it ok to call it in destroy() even if it's removed in dbValueChanged();
If it is of concern and you think there could be multiple listeners as Organis suggests you can check to see if the listener exists.
flash as3 check event listener

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.

remove ENTER_FRAME EventListener from inside this as3

This is my code in Flash/AS3, in main class.
addEventListener(Event.ENTER_FRAME,function(e:Event){
if(findObject == true){
// I want to remove this ENTER FRAME
}
});
try this:
e.currentTarget.removeEventListener(e.type, arguments.callee)
You shouldn't be doing what you do in the code above.
The mgraph's code has a tiny chance of failing to work as advertised if the currentTarget of the event doesn't have a removeEventListener() method (possible, but very unlikely). From the compiler standpoint though you will be trying to dynamically resolve the method on a generic object which is error prone and should be handled with care. This is hazardous because it shows that the programmer "did not know" what kind of object was she expecting to handle and worked by assumption. Assumptions are great for finding a solution but are equally bad for implementing one.
If you thought of optimizing something in the way you did it, then, just FYI this actually creates a unique (redundant) name in the symbol table (in the compiled SWF file) which causes worse compression of the SWF.
If you are doing this as a matter of experiment, this is fine, but you should avoid such code in real life projects.
One more thing to be aware of: comparison to true constant is 100% useless. If such comparison makes any sense at all (i.e. findObject may evaluate to false any time), then if (findObject) { ... } is equivalent but shorter version of your code.
Last thing, hopefully, the anonymous function is missing return type declaration. It won't really change much in your example, except that you will get compiler warning. Omitting type declaration is, in general, a bad style.
EDIT
public function addEventListener(type:String, listener:Function ...):void
{
this._listeners[type].push(listener);
}
public function dispatchEvent(event:Event):void
{
for each (var listener:Function in this._listeners[event.type])
listener(event);
}
public function removeEventListener(type:String, listener:Function, ...):void
{
delete this._listeners[type][listener];
}
Suppose you actually want to implement IEventDispatcher (instead of using another EventDispatcher - you may have your reasons to do so, one such reason is that native EventDispatcher generates insane amounts of short-lived objects - events, and you may want to reduce that.) But there is no way you can replicate event.target or event.currentTurget in your code because you can't access the object owning the method, so, you would leave that out.
Another example:
public class SomeEvent extends Event
{
private var _target:NotEventDispatcher;
public function SomeEvent(type:String, someTarget:NotEventDispatcher)
{
super(type);
this._target = someTarget;
}
public override function get target():Object
{
return this._target;
}
}
This is something that I actually saw in real world, this was used in either Mate or similar framework to sort of "anonymously" connect all event dispatchers to a single static instance of some "mothership event dispatcher".
I don't necessarily justify this approach, but, technically, nothing stops you from doing either one of these. What I was saying in my post above is that in certain situations the language promises you things, like, if you did:
var dispatcher:IEventDispatcher;
try
{
dispatcher = IEventDispatcher(event.currentTarget);
// now you can be sure this object has removeEventListener
dispatcher.removeEventListener(event.type, arguments.callee);
}
catch (error:Error)
{
// but what are you going to do here?
}
But the most common case would be you subscribing to a bubbling event, in which case, you don't know whether you want to unsubscribe from event.target or event.currentTtarget - because you don't know which one is that you are listening to.
I agree with wvxvw.
Another way to approach your problem is to have a variable to control the "state" of your ENTER_FRAME event:
private var _state:String;
private function init(e:Event):void {
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
}
private function loop(e:Event):void {
switch(_state) {
case "play":
// do play stuff
// when you want to pause
// goToPause();
break;
}
}
// you can call the method below from a button or whatever you want
private function goToPause():void {
_state = "pause";
// do some stuff here
// when you are done, switch "_state" back to "play"
}
In this example, you keep listening for ENTER_FRAME, but it only does things when the _state variable is set to "play". You can also remove the event listener in the goToPause method:
private function goToPause():void {
_state = "pause";
removeEventListener(Event.ENTER_FRAME, loop);
}
However, the nice thing about using the "_state" to switch things is that you don't end up having a mess of addEventListeners and removeEventListeners (which is what can happen depending on how complicated your loop gets) that you have to keep track of.
You should not use anonymous function call if you would like to remove listener some time later.
public function main():void
{
//...
//some method, where you add event listener
//...
//adding enterFrame event listener
this.addEventListener(Event.ENTER_FRAME,enterFrameHandler);
//...
}
private function enterFrameHandler(e:Event)
{
if(findObject) // " == true" is not really necessary here.
{
// removing enterFrame listener:
this.removeEventlistener(Event.ENTER_FRAME,enterFrameHandler);
}
}
Just for a completeness with the other techniques mentioned here, the function you are creating is a unbound closure, so you can also leverage that concept to reference both your function and dispatcher.
var callback:Function;
var dispacher:IEventDispatcher = this;
addEventListener(Event.ENTER_FRAME, callback = function(e:Event){
if(findObject == true){
dispacher.removeEventListener(Event.ENTER_FRAME, callback);
}
});
Normal closed-over variable rules apply.