Custom Event from Item Renderer not detected by a class - actionscript-3

I am trying to dispatch a custom event from an item renderer(which is a child of the Main application file / root).
Code in Main.mxml:
<s:List id="movieGrid"itemRenderer="views.MovieRenderer" dataProvider="{new ArrayCollection()}">
</s:List>
<s:Group width="100%" height="100%" bottom="60">
<views:DetailedViewInfo id="detailed" includeIn="MoviePage" />
</s:Group>
Renderer (something clicked):
MyEventDispatcher.Dispatcher.dispatchEvent(new MovieClickEvent(MovieClickEvent.CLICKED, data));
DetailedViewInfo (creation complete):
MyEventDispatcher.Dispatcher.addEventListener(MovieClickEvent.CLICKED, clickHandler);
MyEventDispatcher:
package events
{
import flash.events.EventDispatcher;
public class MyEventDispatcher
{
public static var Dispatcher:EventDispatcher = new EventDispatcher();
}
}
Event:
package events
{
import flash.events.Event;
public class MovieClickEvent extends Event
{
public function MovieClickEvent(type:String, theMovieData:Object, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
this._result = theMovieData;
}
public function get result():Object
{
return this._result;
}
override public function clone():Event
{
return new MovieClickEvent(type, result, bubbles, cancelable)
}
public static const CLICKED:String = "MovieClickEvent.CLICKED";
private var _result:Object;
}
}
I am able to listen for the event successfully in the Main.mxml but I also need to detect it in a SkinnableContainer - "DetailedViewInfo" that is also a child of Main.mxml:
It his possible at all? I tried importing all related events / classes and same for declarations. It does not work even if I comment out the event listener in Main.mxml. I tried adding a declaration to the item renderer in DetailedViewInfo but that crashes the application with no understandable error.
Could someone explain to me how this should be done? I am using custom events all over the place in my application and hadn't had this happen before. Any help highly appreciated!

It would seem you're adding the event listener after the event was dispatched. I see you have an includeIn statement there: this means the DetailedViewInfo component will not be immediately created, but only when the MoviePage state is entered. The event may be dispatched before the component is created and the event listener attached.
The quick fix for this issue, is to not use includeIn, but set the component's visibility according to the current state:
<views:DetailedViewInfo id="detailed" visible="false" includeInLayout="false"
visible.MoviePage="true" includeInLayout.MoviePage="true" />
However, you may want to review your architecture if you need to resort to this. Unfortunately I can't tell you much more than that, since I don't know your current architecture.

Related

Adobe/Apache Flex: Modify View in an ActionScript class

I have a WindowedApplication in Apache/Adobe Flex 4 which currently consists of one view (the view defined in the WindowedApplication MXML).
In that application I have an object which listens to data coming from a network. When data is available a method is called on that object and it shall update my view by changing the text of a label.
I do not have a reference to the view in the network listener object though. How can I get it?
This is part of my MXML where I define my view.
<fx:Script source="./ViewCodeBehind.as"/>
<!-- ommited stuff -->
<s:Label id="errorLabel"
text=""
fontSize="14"/>
<!-- Stuff in between -->
<s:Button label="Get Status"
click="getStatus();"/>
The code which is called when the button is clicked:
public function getStatus(): void
{
var networkGateway: NetworkGateway = new NetworkGatewayImpl();
networkGateway.getConnectionStatus();
}
And the NetworkGatewayImpl
public class NetworkGatewayImpl implements NetworkGateway
{
public function NetworkGatewayImpl()
{
}
public function getConnectionStatus(): void
{
// Start asynchronous network call
// when error occurs onNetworkError() is called
}
private function onNetworkError(): void
{
// Set "errorLabel" here: How?
}
}
Essentially I want to know some ways to update "errorLabel" from the NetworkGatewayImpl.
Based on your code, there could be multiple ways to solve this. Easiest way (as per me) would be to dispatch an event from the NetworkGatewayImpl class and listen to it on the instance you have created in the view class. So sample code would look like this:
public function getStatus(): void
{
var networkGateway: NetworkGateway = new NetworkGatewayImpl();
networkGateway.addEventListener("networkError", onNetworkError);
networkGateway.getConnectionStatus();
}
private function onNetworkError(e:Event):void
{
networkGateway.removeEventListener("networkError", onNetworkError);
this.errorLabel.text = "Your Text Here";
}
Dispatch your event like this from your NetworkGatewayImpl class:
private function onNetworkError(): void
{
this.dispatchEvent("networkError");
}
You will have to ensure that your NetworkGatewayImpl also implements the IEventDispatcher interface to be able to dispatch events.
Also, best practice would be to create a custom Event class (extending the Event class) and use constants instead of the literal 'networkError'
Hope this helps.

Troubles with Custom Events

I've been having lots of trouble with custom events in as3 recently, I've googled a bunch of stuff that doesn't help and used this tutorial to try to figure them out but I'm still confused.
So I set up a new flash fla file to test them and couldn't figure it out, here is what I have:
document as file:
package
{
import flash.events.MouseEvent;
import flash.display.MovieClip;
public class Custom extends MovieClip
{
var intializer:Initializer;
var added:Added;
public function Custom()
{
intializer=new Initializer();
addChild(intializer);
intializer.addEventListener(MouseEvent.CLICK, OnClicker);
addEventListener(CustomEvent.EVENT_CUSTOM, OnCatch);
}
private function OnClicker(event:MouseEvent):void
{
added=new Added();
added.x=300; added.y=300;
addChild(added);
}
private function OnCatch(event:CustomEvent):void
{
trace("hi");
removeChild(added);
}
}
}
event as file:
package
{
import flash.events.Event;
public class CustomEvent extends Event
{
public static const EVENT_CUSTOM="event1";
public function CustomEvent(type)
{
super(type, false, false);
}
}
}
and the movieclips as file:
package
{
import flash.events.MouseEvent;
import flash.display.MovieClip;
public class Added extends MovieClip
{
var addedButton:AddedButton;
public function Added()
{
addedButton=new AddedButton();
addedButton.x=30; addedButton.y=30;
addChild(addedButton);
addedButton.addEventListener(MouseEvent.CLICK, OnClickie);
}
private function OnClickie(event:MouseEvent):void
{
dispatchEvent(new CustomEvent(CustomEvent.EVENT_CUSTOM));
}
}
}
Buttons use an empty class, This gives me this result: (top left corner for first button.)
http://www.fastswf.com/_EfGSoQ
Sorry for so much code, but custom events seem to require a lot of code.
The problem seems to be that you are listening for your custom event in the wrong place. You can address this several ways using event bubbling, the event's capture phase, or by listening for the event on the object that dispatches the event (the event target).
The Flash Event Model follows the W3C event model (used in the DOM/Javascript). When an event is dispatched, it goes through three phases: capture phase, target phase, and bubbling phase. It's described in the above link (in the "Event propagation and phases" section).
For performance reasons (my assumption), the capture and bubbling phases are not enabled by default.
Note you only need to do one of the 3 things below. I suggest using the event target, it's the easiest to understand. The others have their places and can be very useful, but you can do most everything w/the target phase.
How to use the target phase
In your document class remove this line from the constructor:
addEventListener(CustomEvent.EVENT_CUSTOM, OnCatch);
Remove it because we're going to listen for the event coming from the Added object directly (it's the event target). To do that change your code that adds the object in the document class:
private function OnClicker(event:MouseEvent):void
{
added=new Added();
added.x=300; added.y=300;
addChild(added);
added.addEventListener(CustomEvent.EVENT_CUSTOM, OnCatch);
}
How to use the capture phase
In your document class, add an additional parameter when adding the event listener to enable the capture phase:
addEventListener(CustomEvent.EVENT_CUSTOM, OnCatch, true);
This allows any parent of the event target to handle the event, before the target handles it.
How to use the bubbling phase:
To use the bubbling phase, your custom event object needs to "bubble". So you will need to modify the constructor in your custom event class:
public function CustomEvent(type)
{
super(type, true, false);
}
Here I've changed the second parameter in the call to super(), allowing the event to "bubble".
Since the event now bubbles back up the display list, any parent of the event target will also be able to listen for the event after the target has handled it.

[Event(name=“”)]: How is it used and how do custom events work?

I have to work on a project someone else started, but can't contact him because he's out of the country at the moment. Anyway. There is a main mxml and a custom component called "admin".
In admin he declared an event like this:
<fx:Metadata>
[Event(name="sluitFrame")]
</fx:Metadata>
And in a certain function within the component, this event is called upon like this:
dispatchEvent(new Event("sluitFrame"));
This event sluitFrame closes a frame with some admin tools. I need to change the way this works so i'd like to find the corresponding code. On the main mxml there's this code:
<comp:Admin id="compAdmin" creationPolicy="none"
sluitFrame="verbergAdminComponent(event)"/>
So if i understand correctly sluitFrame calls to a custom even called "verbergAdminComponent(event)". So i guess i need this event to change the way the admin frame is closed etc. But this event is nowhere to be found. So I don't understand how "verbergAdminComponent(event)" works or where I can make changes to this event.
Any help is more than welcome and very much needed :)
The [Event... line simply lets the compiler and IDE know that the component/class (in this case, Admin) may dispatch an event by that name. This is important because when someone declares an instance of Admin in a MXML tag, the compiler knows that this event (in this case, sluitFrame) is a valid property. In other words, it lets the compiler know that you can set an event listener in the MXML tag. In your case, every time the Admin object dispatches a sluitFrame event, the function verbergAdminComponent is called and the sluitFrame Event is passed to it.
verbergAdminComponent would be the name of an event handler. It should be a method either in the mxml, in an included .as, or in a base class of that MXML.
Types in Events
If you use an Event in Flash, you could dispatch any named type, since it is a string. So it does not matter how you call it, as long as the listener listens to the exact same type. That's why it works. There is no magic evolved. However, I would choose to use an custom event in that case.
How custom events work
Take a look at custom events work, so you also understand where meta data comes in.
package com.website.events
{
import flash.events.Event;
/**
* #author ExampleUser
*/
public class MyCustomEvent extends Event
{
/**
*
*/
public static const CLOSE_WINDOW:String = "MyCustomEvent.closeWindow";
public function MyCustomEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false):void
{
super(type, bubbles, cancelable);
}
override public function clone():Event
{
return new MyCustomEvent(this.type, this.bubbles, this.cancelable);
}
override public function toString():String
{
return formatToString("MyCustomEvent", "type", "bubbles", "cancelable", "eventPhase");
}
}
}
I use a static constant instead of directly a string (like your example), so it is typed more strictly.
In actionscript, you'd dispatch the custom event like this. Let's say you create a class named Window.
package com.website.ui
{
/**
* #eventType com.website.events.MyCustomEvent.closeWindow
*/
[Event(name="MyCustomEvent.closeWindow", type="com.website.events.MyCustomEvent")]
import flash.display.Sprite;
import flash.events.MouseEvent;
import com.website.events.MyCustomEvent;
/**
* #author ExampleUser
*/
public class Window extends Sprite
{
public var mcCloseButton:Sprite;
public function Window():void
{
this.mcCloseButton.addEventListener(MouseEvent.CLICK, handleCloseButtonClick);
}
private function handleCloseButtonClick(event:MouseEvent):void
{
this.dispatchEvent(new MyCustomEvent(MyCustomEvent.CLOSE_WINDOW));
}
}
}
This is the class where the meta data should be located. This class is dispatching the event. Other classes that dispatches the same event, could have the meta-data too.
So, Window is dispatching an event with type CLOSE_WINDOW when user clicked on the close button. In another file you would listen to it and do something with it.
package com.website
{
import flash.display.Sprite;
import com.website.events.MyCustomEvent;
import com.website.ui.Window;
/**
* #author ExampleUser
*/
public class Main extends Sprite
{
private var _window:Window;
public function Main():void
{
this._window:Window = new Window();
// a smart code-editor would give you a hint about the possible events when you typed "addEventListener"
this._window.addEventListener(MyCustomEvent.CLOSE_WINDOW, handleWindowClosed);
this.addChild(this._window);
}
private function handleWindowClosed(event:MyCustomEvent):void
{
// do something
this._window.visible = false;
}
}
}
This should work.
Ofcourse in a real-world situation MyCustomEvent would be named WindowEvent.
Event Meta Data
The meta data could be used to give hints to the compiler and nowadays smart code editors (FDT, FlashDevelop, FlashBuilder, IntelliJ etc.) can give code completion. It's basically a description of what kind of events may be dispatched by a class, so you know what listeners could be used.
The code should work even when the meta data is deleted.
The Event meta has a name and a type. The name should be the exact value of the type. In the case of our example, it should be the value of CLOSE_WINDOW, so that's MyCustomEvent.closeWindow.
The type should be the classname with the full package, in the case of our example it would be 'com.website.events.MyCustomEvent'.
Finally, the meta data looks like this:
[Event(name="MyCustomEvent.closeWindow", type="com.website.events.MyCustomEvent")]
BTW I have some tips about your code:
I would suggest to use English function names and parameters, instead of Dutch.
verbergAdminComponent isn't a good name for a handler, it should be something like handleCloseWindow(event), which should call the verbergAdminComponent function.

AS3 event across multiple files

I'm migrating from AS2 to AS3 and have this problem..
In my project I used main document file, which could load multiple nested animations as a separate files. In the main document I had a global function, for example:
_global.onAnimationEnd(mc:MovieClip){...}
and in animations (could be nested) I just called it like:
onAnimationEnd(this);
I'm new with AS3 but somehow thing that the proper way in AS3 is to use Event system, but I have problems to do it. Is there someone who can help with such an easy(AS2) issue?
The way that AS3 handles events is quite different to the AS2 approach. In AS3 events bubble up the object hierarchy and then back down to the originating object. If you have objects further up the hierarchy that are required to respond to an event it is necessary to set up event listeners on the recipient objects to handle the event as it bubbles.
Colin Moock's book Essential ActionScript 3.0 published by O'Reilly deals with it comprehensively. Not much help if you need a quick fix right now though…
Finally I'm using this approach:
I'm using custom event like:
package com.oldes {
import flash.events.Event;
public class GameEvent extends Event {
public var data:Object;
public static var ANIMATION_END = "onAnimationEnd";
public function GameEvent(
type:String,
data: Object,
bubbles:Boolean=true,
cancelable:Boolean=false
){
super(type, bubbles, cancelable);
this.data = data;
}
override public function clone():Event {
return new GameEvent (type, data, bubbles, cancelable);
}
}
}
In my deep nested animations I replaced:
onAnimationEnd(this);
with:
import com.oldes.GameEvent;
dispatchEvent(new GameEvent(GameEvent.ANIMATION_END,{anim: this}));
Using clasic listeners to deal with the cached event.
EDIT: the external file with animations must be properly loaded into the correct context, but that's another AS3 story.

Flex event handler not working

I have created a custom event in flex 3.5. But the handler is not invoked. How to solve this or what is the way to debug this problem?
The Event class:
package com.saneef.worldlanguages.events
{
import flash.events.Event;
public class LanguageEvent extends Event
{
public static const LANGUAGE_SELECTED:String = "LanguageSelected";
public function LanguageEvent(type:String,languageid:String)
{
super(type);
this.langid = languageid;
trace("LanguageEvent: " + this.langid);
}
public var langid:String;
override public function clone():Event {
return new LanguageEvent(type, langid);
}
}
}
Dispatching:
private function functionOne():void
{
try{
dispatchEvent(new LanguageEvent(LanguageEvent.LANGUAGE_SELECTED,"STR"));
}
catch(e:Error)
{
trace(e.message);
}
}
In the Main application class, EventListener:
protected function application1_initializeHandler(event:FlexEvent):void
{
this.addEventListener(LanguageEvent.LANGUAGE_SELECTED,
application1_LanguageSelectionHandler);
}
The event handler function:
public function application1_LanguageSelectionHandler(event:LanguageEvent):void
{
trace("application1_LanguageSelectionHandler: " + event.langid);
populate_countrya3id_languages(event.langid);
}
Your code looks fine. Since I can't see the full source, here are my two thoughts on what may be going on:
Are you sure your addEventListener call is done before you dispatch the event? Add some trace to make sure the application1_initializeHandler prints before functionOne does.
Is your functionOne call in another different component than your main application? If so, you'll need to set your custom event's bubbles attribute to true in your event's super call.
public function LanguageEvent(type:String,languageid:String,bubbles:Boolean=True)
{
super(type, bubbles);
this.langid = languageid;
trace("LanguageEvent: " + this.langid);
}
See the flash.events.Event docs for the constructor call. Also, here's a quote about the bubbles argument explained here:
The bubbles property
An event is said to bubble if its
event object participates in the
bubbling phase of the event flow,
which means that the event object is
passed from the target node back
through its ancestors until it reaches
the Stage. The Event.bubbles property
stores a Boolean value that indicates
whether the event object participates
in the bubbling phase. Because all
events that bubble also participate in
the capture and target phases, any
event that bubbles participates in all
three of the event flow phases. If the
value is true, the event object
participates in all three phases. If
the value is false, the event object
does not participate in the bubbling
phase.
Based on your source code, it looks like you've seen the "Dispatching Custom Events" in the flex docs, but I'll link to it anyways for future/easy reference: http://livedocs.adobe.com/flex/3/html/help.html?content=createevents_3.html.
Also, check out http://www.adnandoric.com/2008/12/29/understanding-the-flex-event-propagation/ for a high-level overview of the event propagation system to try to get a better understanding of what's going on while developing.
Edit:
Based on your comments I'm guessing your functionOne call is in a separate class and your "main" application has an instance of this class. If that's so you'll want to attach your event listener on that instance and delegate it to your main's application1_LanguageSelectionHandler function... Like so:
protected function application1_initializeHandler(event:FlexEvent):void
{
this.theInstanceThatHoldsYourFunctionOne.addEventListener(LanguageEvent.LANGUAGE_SELECTED,
application1_LanguageSelectionHandler);
}