How do i access a file that is loaded after? - actionscript-3

This is in Flash Action Script 3
Here is my class, I loaded an xml in the class
and now i want to do some things after in the main page, like create things on the stage based off of the xml. Help
I understand why it doesn't work, by the time the function is called the xml hasn't be loaded yet, i'm just clueless how to solve this? add an event listener maybe?
package {
public class LoadXMLAdventure {
var adventureXML: XML;
var xmlReq:URLRequest;
var xmlLoader:URLLoader = new URLLoader();
var _currentRoom:int;
public function LoadXMLAdventure(xmlFileName:String) {
xmlReq = new URLRequest(xmlFileName);
xmlLoader.load(xmlReq);
xmlLoader.addEventListener(Event.COMPLETE, xmlLoaded);
}
function xmlLoaded(event:Event):void{
adventureXML = new XML(xmlLoader.data)
//trace( adventureXML.toXMLString() )
}
public function myFunction():void{
trace(adventureXML.toXMLString()) //this does not work
}
}
}
On my main page
var game:LoadXMLAdventure = new LoadXMLAdventure("file.xml");
game.myFunction();
Update
I want to call a function on the main page so i can do some stage manipulation
this is in the class, i get an error undefined method
function xmlLoaded(event:Event):void{
adventureXML = new XML(xmlLoader.data)
this.dispatchEvent( new Event("showRoom") );
}
this is on the stage
stage.addEventListener("showRoom",showRoom);
function showRoom(e:Event):void{
trace("showroom hello")
}

You can dispatch an event when the xml loaded complete,but in your case, you just need to call myFunction in xmlLoaded;
import flash.events.EventDispatcher;
public class LoadXMLAdventure extends EventDispatcher{
public function LoadXMLAdventure(xmlFileName:String) {
xmlReq = new URLRequest(xmlFileName);
xmlLoader.addEventListener(Event.COMPLETE, xmlLoaded);
}
public function startLoad():void {
xmlLoader.load(xmlReq);
}
function xmlLoaded(event:Event):void{
adventureXML = new XML(xmlLoader.data);
this.dispatchEvent(new Event("LoadXmlComplete"));
}
}
Here is how to use
var game:LoadXMLAdventure = new LoadXMLAdventure("file.xml");
game.addEventListener("LoadXmlComplete", onComplete);
game.startLoad();
private function onComplete(e:Event):void {
//do some thing
}

Related

synchronous image loading action script 3 flash

I am having a problem with my flash/as3 application. I've created most of it and at the moment I'm struggling to get my external resources work.
My application consists of a Controller class, that takes control of an application flow. At the beginning it initializes AppInitializer class, that loads / generates the whole app content (it is a simple point'n'click game).
In AppInitializer I create an array of items available in a game. Item's constructor takes path as a parameter (String) to the resource (image). Then, inside the constructor I call a static method of my AssetsLoader class which looks like that:
public static function loadImage(path:String):BitmapData
{
completed = false;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event){completed = true;
trace("Loading completed");
e.target.removeEventListener(Event.COMPLETE, check);});
if (path == "")
loader.load(new URLRequest("images/default.png"));
else
loader.load(new URLRequest("images/" + path));
//while (!completed);
var image:Bitmap = new Bitmap((loader.content as BitmapData));
return image.bitmapData;
}
Where completed is a static variable of AssetsLoader.
First problem is: I create many Item objects in a row, so the method loadImage should not be static I guess (same with completed variable), since that may cause problems when loading.
Second problem is: At the moment I'm unable to return the bitmapData (or bitmap, it does not really matter), because the return statement will always return null - because the resource is loaded asynchronously and is not loaded at the time application reaches return statement. If I uncomment the wile loop, the complete event is never called.
I would like to ask more experienced ActionScript developers to point out any solutions that would require minimal changes to the rest of my app and would solve those problems. (I think first problem can be eliminated by using some sort of queueing method for the loader, but I'm stuck with the second problem for few days so far and came up with no possible solution).
I could also consider changes in my logic, so I could preload all image resources into "somewhere" and after that just make copies of these images for my purposes. If that's easier to do.
So as I suggested in the comments, a minimal change resolution could simply be to pass a function to call as part of the parameters for loadImage(). This is known as a callback function, and it would look something like this:
First create the callback function:
public function addImage( bitmapData:BitmapData ):void {
//do something with the bitmapData returned
}
Next adjust your loadImage() local function to use the callback with the bitmap data when the event has completed:
public static function loadImage(path:String, callback:Function):BitmapData {
completed = false;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event){completed = true;
trace("Loading completed");
var image:Bitmap = new Bitmap((loader.content as BitmapData));
callback( image ); //call the callback with the bitmap
e.target.removeEventListener(Event.COMPLETE, check);});
if (path == "")
loader.load(new URLRequest("images/default.png"));
else
loader.load(new URLRequest("images/" + path));
}
Then just you make the call to loadImage() like so:
loadImage( myPathToImage, addImage );
That is a simply resolution and does exactly what you need it to.
Super, you commented that insane line of code with while ;)
Here for you, simple QueueLoader (It loads items one by one, when you add item to the queue, you can store id of item in queue), that will help you with your task:
package {
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
public class StackOverflow extends Sprite {
public function StackOverflow() {
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, onAdded);
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
setup();
}
private function setup():void {
//Store somewhere reference on QueueLoader to reuse it, and use queue
var loader:QueueLoader = new QueueLoader();
loader.addEventListener(QueueLoaderEvent.COMPLETE, onCompleteItem);
//Add as many images to load as you want, and store Id's of items that
//will be loaded in future, if you want...
loader.addItem("someUrl1");
loader.addItem("someUrl2");
var importantId:int = loader.addItem("someUrl3");
loader.addItem("someUrl4");
loader.addItem("someUrl6");
}
private function onCompleteItem(e:QueueLoaderEvent):void {
trace("Item loaded");
}
}
}
import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.net.URLRequest;
internal class QueueLoader extends EventDispatcher {
private var _list:Array;
private var _cursor:int;
private var _loading:Boolean;
public function QueueLoader(target:IEventDispatcher = null) {
super(target);
_list = [];
}
public function addItem(url:String):int {
var item:Object = {url: url, id: ++_cursor};
_list.push(item);
loadNext();
return item.id;
}
override public function dispatchEvent(evt:Event):Boolean {
if (hasEventListener(evt.type) || evt.bubbles) {
return super.dispatchEvent(evt);
}
return true;
}
protected function loadNext():void {
if (_list.length > 0 && !_loading) {
var loader:Loader = new Loader();
var data:Object = _list[0];
var request:URLRequest = new URLRequest(data.url);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete, false, 0, true);
loader.load(request);
_loading = true;
}
}
private function onComplete(e:Event):void {
var data:Object = _list.shift();
data.content = e.currentTarget.content;
dispatchEvent(new QueueLoaderEvent(QueueLoaderEvent.COMPLETE, data.id, data));
_loading = false;
loadNext();
}
}
internal class QueueLoaderEvent extends Event {
public static const COMPLETE:String = "queueLoaderEventComplete";
private var _id:int;
private var _data:Object;
public function QueueLoaderEvent(type:String, $id:int, $data:Object, bubbles:Boolean = false, cancelable:Boolean = false) {
_id = $id;
_data = $data;
super(type, bubbles, cancelable);
}
override public function clone():Event {
return new QueueLoaderEvent(type, id, data, bubbles, cancelable);
}
public function get id():int {
return _id;
}
public function get data():Object {
return _data;
}
}
Your loadImage mehtod will look at the end:
public static function loadImage(path:String):int
{
return queueLoader.addItem(path);
}

AS3. Return a value with Loader Complete Event

I want to make some function, with returning loading object.
Something like:
var myVar:String;
myVar = MyFuncs.GetResponse("http://www....");
And GetResponse function must return some string value, for example json-like text.
I try.. But cant understend.
public function GetResponse(url:String):String{
var request:URLRequest = new URLRequest(url);
var loader:URLLoader = new URLLoader();
loader.load(request);
return loader.data
}
But data is not loaded yet, when I return the value.
I understand, I need to add listener, when loader is complete:
loader.addEventListener(Event.COMPLETE, Complete);
But cant understand, how can I return the loaded value, when the loading is complete.
Because it will be a another function..
Please, help, if someone know, how :)
Sorry for my English, please.
You can create a custom loader, and set a callback function to it, when the loader load complete, the callback will be executed. Here is a simple example
public class MyLoader extends Loader
{
public function MyLoader($callBack:Function = null)
{
super();
callBack = $callBack;
this.contentLoaderInfo.addEventListener(Event.COMPLETE, Complete);
}
private var callBack:Function;
private var _url:String;
public function set url(value:String):void {
if (_url != value) {
_url = value;
var request:URLRequest = new URLRequest(_url);
this.load(request);
}
}
protected function Complete(event:Event):void {
var target:Object = event.target;
if (callBack) {
callBack.apply(null, [target]);
}
}
And you can use it like this in class A
public function class A {
public function test():void {
var loader:MyLoader = new MyLoader(setData);
loader.url = "assets/pig.jpg";//you asset url
}
private function setData(obj:Object):void {
//the obj type is LoadInfo
}
}

How to remove a event listener with an unknown function in as3?

My idea in a small upload software is to use always the same object for all tasks (defined before), I just add and remove the events and make the requests, since the parameters are always the same (same method, same url...).
Any time the request is completed, I remove the listeners so the same object can be used again.
The problem is when some error occurs, than the listener call the the function ioerror, but I don't know what function should be called instead if there was no error:
private function ioerror(e:IOErrorEvent){
e.target.removeEventListener(Event.COMPLETE, unknownfuncion);
e.target.removeEventListener(IOErrorEvent.IO_ERROR, ioerror);
msg("Error somewhere ("+e.text+")");
}
How to get the name of "unknownfunction" ? My fear is to leave events behind...
You could set up a couple of simple classes to manage a collection of event listeners. Lets call the collection EventBatch, which could look like this:
public class EventBatch
{
private var _items:Vector.<EventBatchItem> = new <EventBatchItem>[];
public function addListener(target:IEventDispatcher, type:String, callback:Function):void
{
var item:EventBatchItem = new EventBatchItem(target, type, callback);
_items.push(item);
target.addEventListener(type, callback);
}
public function removeAll():void
{
for each(var i:EventBatchItem in _items)
{
i.target.removeEventListener(i.type, i.callback);
i.dispose();
}
_items = new <EventBatchItem>[];
}
}
And here's the accompanying model to represent an item:
internal class EventBatchItem
{
private var _target:IEventDispatcher;
private var _type:String;
private var _callback:Function;
public function EventBatchItem(target:IEventDispatcher, type:String, callback:Function)
{
_target = target;
_type = type;
_callback = callback;
}
internal function dispose():void
{
_target = null;
_callback = null;
}
internal function get target():IEventDispatcher{ return _target; }
internal function get type():String{ return _type; }
internal function get callback():Function{ return _callback; }
}
This way, you can add your event listeners like this:
var batch:EventBatch = new EventBatch();
batch.addListener(urlLoader, Event.COMPLETE, completeHandler);
batch.addListener(urlLoader, SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
batch.addListener(urlLoader, IOErrorEvent.IO_ERROR, ioErrorHandler);
And in any of those listener functions, simply use the .removeAll() method:
batch.removeAll();
If you known all the functions that could possibly be added as a Listener, you can just remove all of them. Using removeEventListener() with a method that is not actually listening do nothing. So you could use something like that :
private function ioerror(e:IOErrorEvent){
// I know that Event.COMPLETE could be listened by function1, function2 or function3
// I remove all of them
e.target.removeEventListener(Event.COMPLETE, function1);
e.target.removeEventListener(Event.COMPLETE, function2);
e.target.removeEventListener(Event.COMPLETE, function3);
e.target.removeEventListener(IOErrorEvent.IO_ERROR, ioerror);
msg("Error somewhere ("+e.text+")");
}
Another possibility is to keep track of the method(s) actually listening the event in a variable.
public function doSomething() {
loader.addEventListener(Event.COMPLETE, onCompleteSomething);
listeningComplete= onCompleteSomething;
}
public function doSomethingElse() {
loader.addEventListener(Event.COMPLETE, onCompleteSomethingElse);
listeningComplete= onCompleteSomethingElse;
}
private function ioerror(e:IOErrorEvent){
e.target.removeEventListener(Event.COMPLETE, listeningComplete);
e.target.removeEventListener(IOErrorEvent.IO_ERROR, ioerror);
msg("Error somewhere ("+e.text+")");
}
private var listeningComplete:Function;
Assuming the handler is reset and an instance to the object is retained, you could simply set useWeakReference to true in your addEventListener() function for garbage collection.
However, a better design pattern is to abstract service methods to a class.
Then, calling dispose can remove all handlers.
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
public class AbstractService extends EventDispatcher
{
public var data:Object;
public var requestMethod:String = URLRequestMethod.GET;
public var url:String;
protected var urlLoader:URLLoader;
protected var urlRequest:URLRequest;
public function AbstractService()
{
super();
urlLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, completeHandler);
urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
}
/**
*
* #param url
* #param data String or URLVariables
*/
public function load(url:String=null, data:Object=null, requestMethod:String=URLRequestMethod.GET):void
{
if (url)
this.url = url;
if (data)
this.data = data;
if (requestMethod)
this.requestMethod = requestMethod;
urlRequest = new URLRequest(this.url);
urlRequest.data = this.data;
urlRequest.method = this.requestMethod;
urlLoader.load(urlRequest);
}
protected function completeHandler(event:Event):void
{
}
protected function ioErrorHandler(event:IOErrorEvent):void
{
}
protected function securityErrorHandler(event:SecurityErrorEvent):void
{
}
public function dispose():void
{
urlLoader.removeEventListener(Event.COMPLETE, completeHandler);
urlLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
}
}
}
Take a look at my previous answer to Is There A Way To Remove Events Easier?. Basically, there is no easy way to do it. In that answer, I put together a simple way to track events that are added to a class by simply overriding the event listener methods. Using that method, you could just call removeAllEventListeners() and be done with it. It works fairly well, so long as you consistently remember to call that function (otherwise things will never get removed from memory).
This is overkill for the majority of situations, however. I'm currently using it for a massive application and it has helped immensely, but for something simple, it is just going to add extra computations and memory consumption to your app that is unneeded.

How to return a variable from an actionscript Event? Loading XML file

i'm trying to load an xml file from my assets folder.
I wrote this function :
public static function loadXML(i_fileURL:String):XML // i want to return the actual loaded xml here
{
var xml:XML;
var ldr:URLLoader = new URLLoader();
var request:URLRequest = new URLRequest(i_fileURL);
ldr.addEventListener(Event.COMPLETE, onXMLLoad);
ldr.load(request);
//how can i return the loaded xml?
}
public static function onXMLLoad(e:Event):void
{
var ldr:URLLoader = URLLoader(e.target);
var myxml:XML = new XML(ldr.data);
trace(myxml.toXMLString());
//how can i return myxml to loadXML function?
}
Is there a different way to do this?
Thank you!
You can do something like a promise or future, where you return empty XML and then populate it with the actual XML when the call returns. Since you are using Flex, you have access to data binding, which should allow this approach to work just fine.
Note that you really shouldn't be using static methods for this, and your onXMLLoad member has no reason to be exposed. Here's what the updated code might look like:
package service {
public class XMLLoader {
//note that the existence of this variable means that you need
//to create a new instance of the Class each time you make a call.
protected var future:XML;
protected var _url:String;
public function loadXML(url:String):XML {
_url = url;
var request:URLRequest = new URLRequest(url);
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.Complete, onLoad);
loader.addEventListener(IoErrorEvent.IO_Error, onFail);
loader.addEventListener(SecurityErrorEvent.Security_Error, onFail);
future = ;
return future;
}
protected function onLoad(e:Event):void {
var loader:URLLoader = e.currentTarget as URLLoader;
var data:XML = loader.data as XML;
if (data) {
//you lose your original root node, because you want data
//binding to fire on the future object you returned.
future.setChildren(data.children());
}
}
protected function onFail(e:Event):void {
//both Security and IOerrors have a text property, but they
//can't both be cast to the same thing.
trace('failed to load', _url, e[text]);
}
}
}
One thing to be aware of with this method is that you need to keep a reference to the instance around until the data has updated, or else it could be garbage collected before it populates the future. Because of that, you're probably better off following the conventional approach of having your instance dispatch a custom event that carries the data that it was retrieving. If you want an example of that, post back and I can provide you an example.
you can't. because xml load request is asynchronous. when you called loadXML, onXMLLoad not yet arrived. so such work impossible asynchronous return. surely you can waiting the function process while loop using, but this method not good. because to full use a cpu resource, overkill. you must next process in onXMLLoad function. It's the most appropriate. or xml variable declared as global, and using a ENTER_FRAME or TimerEvent as a way to continue to operate if the xml is not null.
Since the URLLoader is asynchronous, it's not safe to create a static loader function as it would be quiet easy to mix up returned data during multiple calls. Even if you attempted to accomplish what you want with the use of Event.OPEN and a vector of URLs to keep track of the which completed data should belong to each URL, asynchronousity works on a first-come, first-served basis so it wouldn't be possible to have persistent alignment of the file URL and the returned data.
I suggest that you create instances of an XMLLoader class that uses a custom XMLLoaderEvent, which will return both the xml data and the associated file URL. The following code is untested, but with possible typos aside, it should work as intended.
Use Case
var xmlLoader:XMLLoader = new XMLLoader();
xmlLoader.addEventListener(XMLLoaderEvent.COMPLETE, xmlLoadCompleteEventHandler);
xmlLoader.load("myXMLFile.xml");
function xmlLoadCompleteEventHandler(event:XMLLoaderEvent):void
{
xmlLoader.removeEventListener(XMLLoaderEvent.COMPLETE, xmlLoadCompleteEventHandler);
trace(event.type, event.fileURL, event.xml);
}
XMLLoader.as
package
{
//Imports
import flash.events.EventDispatcher;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
//Class
public class XMLLoader extends EventDispatcher
{
//Properties
private var loader:URLLoader;
private var fileURL:String;
//Constructor
public function XMLLoader():void
{
loader = new URLLoader();
loader.addEventListener(Event.COMPLETE, loadCompleteEventHandler);
}
//Load
public function load(fileURL:String):void
{
this.fileURL = fileURL;
loader.load(new URLRequest(fileURL));
}
//Load Complete Event Hanlder
private function loadCompleteEventHandler(event:Event):void
{
loader.removeEventListener(Event.COMPLETE, loadCompleteEventHandler);
dispatchEvent(new XMLLoaderEvent(XMLLoaderEvent.COMPLETE, fileURL, XML(loader.data)));
}
}
}
XMLLoaderEvent.as
package
{
//Imports
import flash.events.Event;
//Class
public class XMLLoaderEvent extends Event
{
//Constants
public static const COMPLETE:String = "complete";
//Properties
public var xml:XML;
public var fileURL:String;
//Constructor
public function XMLLoaderEvent(type:String, fileURL:String = null, xml:XML = null)
{
super(type);
this.xml = xml;
this.fileURL = fileURL;
}
//Clone
public override function clone():Event
{
return new XMLLoaderEvent(type, fileURL, xml);
}
//To String
public override function toString():String
{
return formatToString("XMLLoaderEvent", "type", "fileURL", "xml");
}
}
}

Broadcast Custom Event from Model

`My "Model" is an AS class that extends EventDispatcher:
MeetingInfoModel extends EventDispatcher
In this class I broadcast a custom event:
var eventObj:CustomEvent = new CustomEvent( CustomEvent.UPDATE_DOC_COUNTER );
dispatchEvent( eventObj );
I include a metadata tag at top of class:
[Event(name="updateDocCounter", type="com.fmr.transporter.events.CustomEvent")]
I attempt to listen to for this event in an MXML component:
this.addEventListener( CustomEvent.UPDATE_DOC_COUNTER, onDocUpdate );
But it never reaches this listener.
I've run into issues like this a lot and I think there's a crucial part of the Event process that I'm just not understanding.
Can anyone provide any helpful clues, please?
Thank you!
UPDATE:
In response to all the comments below (thank you for all the responses!):
MeetingInfoModel is not a display component and shouldn't be responsible for broadcasting events; that's the piece I was not getting!!
Here's my code: In the MeetingInfoModel constructor I listen for the collection change event of one of its class members:
docsAndAttachmentsList.addEventListener( CollectionEvent.COLLECTION_CHANGE, updateDocsCounter );
In that handler I try to broadcast an event that an MXML component (that is part of the display hierarchy) will handle:
private function updateDocsCounter( event:CollectionEvent ):void
{
var eventObj:CustomEvent = new CustomEvent( CustomEvent.UPDATE_DOC_COUNTER );
dispatchEvent( eventObj );
}
Back in the MXML component, I call this method from the creationComplete handler:
private function addListeners():void{
MeetingInfoModel.getInstance().addEventListener( CustomEvent.UPDATE_DOC_COUNTER, onDocUpdate );
}
It sounds like I should just listen for the collection change event on the MXML component. I tried that but it doesn't work:
MeetingInfo.getInstance().docsAndAttachmentsList.addEventListener( CollectionEvent.COLLECTION_CHANGE, updateDocsCounter );
I don't know why that's not working; it seems to be the best solution.
Here's the full MeetingInfoModel class:
[Bindable]
[Event(name="updateDocCounter", type="com.fmr.transporter.events.CustomEvent")]
public final class MeetingInfoModel extends EventDispatcher
{
//Universal INFO
public var generalInfo:GeneralInfoModel;
public var meetingVO:MeetingVO = new MeetingVO();
public var meetingId:String;
public var bulletinBoardLiveMembers:ArrayCollection = new ArrayCollection();
public var xmppServices:XMPPServices;
public var declinedParticipantsGroup:ArrayCollection = new ArrayCollection();
public var notJoinedParticipantsGroup:ArrayCollection = new ArrayCollection();
public var conferenceRoomParticipantsGroup:ArrayCollection = new ArrayCollection();
public var otherLocationParticipantsGroup:ArrayCollection = new ArrayCollection();
[Bindable]
public var documentList:ArrayCollection = new ArrayCollection();
[BIndable]
public var newAttachmentList:ArrayCollection = new ArrayCollection();
public var docsAndAttachmentsList:ArrayCollection = new ArrayCollection();
public var bulletinBoardMsgList:ArrayCollection = new ArrayCollection();
private var _participantList:ArrayCollection = new ArrayCollection();
public var dismissedMeetingIDs:Array = [];
public var visibleToastWindows:Array = [];
public function MeetingInfoModel()
{
generalInfo = GeneralInfoModel.getInstance();
xmppServices = XMPPServices.getInstance();
_participantList.addEventListener(CollectionEvent.COLLECTION_CHANGE, allParticipantsChangeHandler);
bulletinBoardLiveMembers.addEventListener(CollectionEvent.COLLECTION_CHANGE, bulletinBoardLiveMembersChangeHandler);
docsAndAttachmentsList.addEventListener( CollectionEvent.COLLECTION_CHANGE, updateDocsCounter );
}
private static var model:MeetingInfoModel = null;
public static function getInstance():MeetingInfoModel
{
if (model == null)
{
model = new MeetingInfoModel();
}
return model;
}
/**
* The handler for the collection change event of the docsAndAttachmentsList collection.
*
* We use it to manually update the counter on the Docs tab.
*/
private function updateDocsCounter( event:CollectionEvent ):void
{
var eventObj:CustomEvent = new CustomEvent( CustomEvent.UPDATE_DOC_COUNTER );
dispatchEvent( eventObj );
}
public function displayToastForThisMeeting(meetingID:Number):Boolean
{
//trace("model::meetingID = " + meetingID);
var doDisplayToast:Boolean = false;
var containsMeetingID:Boolean = false;
//the first one
if(dismissedMeetingIDs.length == 0)
{
//trace("dismissedMeetingIDs.length = 0");
doDisplayToast = true;
dismissedMeetingIDs.push(meetingID);
}
else
{
for(var i:int=0; i < dismissedMeetingIDs.length; i++)
{
//trace("dismissedMeetingIDs[" + i + "] = " + dismissedMeetingIDs[i]);
if(meetingID == dismissedMeetingIDs[i])
{ //this one has already been dismissed
doDisplayToast = false;
containsMeetingID = true;
break;
}
else
{
doDisplayToast = true;
containsMeetingID = false;
}
}
if(containsMeetingID == false)
{
dismissedMeetingIDs.push(meetingID);
}
}
return doDisplayToast;
}
}
Here's some code from my MXML component (whose base class is Group):
import com.fmr.transporter.controller.TransporterController;
import com.fmr.transporter.events.CustomEvent;
import com.fmr.transporter.model.MeetingInfoModel;
import com.fmr.transporter.model.TransporterModel;
import mx.collections.ArrayCollection;
import mx.core.FlexGlobals;
import mx.events.CollectionEvent;
import mx.events.FlexEvent;
private var controller:TransporterController;
[Bindable] public var newAttachmentsList:ArrayCollection;
[Bindable] public var meetingInfo:MeetingInfoModel;
private function complete():void
{
controller = TransporterController.getInstance();
addListeners();
}
/** Add listeners to this class.
*/
private function addListeners():void{
MeetingInfo.getInstance().docsAndAttachmentsList.addEventListener( CollectionEvent.COLLECTION_CHANGE, updateDocsCounter );
}
You extended the event class. By default, flex event don't bubble. You need to modify your CustomEvent class constructor to look like this:
public function CustomEvent(type:String){
super(type, true, true);
}
This will make your event bubble and also cancelable per flex event framework. #The_asMan told you correctly about how to deal with a non bubbling event, but I think you may have accidentally missed the right object. Make it bubble and you will hear it!!
#LondonDrugs_MediaServices, #Flextras, and #The_asMan.
These guys are right; it doesn't appear that the class has to be on the display list in order for the event to be listened for. As long as you have the correct target for the class dispatching the event it should be just fine.
For some reason, though, I've done something really weird and couldn't quite figure it out in the end. The solution was to use binding on the collection I updated in the model in order to update the component I wanted.
Thank you everyone for your very helpful comments.