Getting data from custom classes (an OOP question) - actionscript-3

How can I get some var / data from a custom classes?
The XML class
package classes
{
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.display.MovieClip;
public class videoData extends MovieClip
{
private var myXML:XML;
private var myXMList:XMLList;
public function videoData()
{
var myXMLLoader:URLLoader = new URLLoader();
myXMLLoader.load(new URLRequest("playlist.xml"));
myXMLLoader.addEventListener(Event.COMPLETE, processXML);
}
private function processXML(e:Event):void
{
myXML = new XML(e.target.data);
myXMList = new XMLList(myXML.children());
}
public function getXML()
{
return myXML;
}
}
}
The class that is calling the XML
package classes
{
import flash.display.MovieClip;
import flash.events.MouseEvent;
import classes.videoData;
public class playList extends MovieClip
{
private var vData:videoData = new videoData();
public function playList()
{
trace(vData.getXML())
}
}
}

I would setup an event listener in 'playList' and dispatch an Event from 'videoData' once the XML has finished loading. That way you know when it's finished loading without using ENTER_FRAME (which will use alot more CPU as its checking every frame).
package classes
{
import flash.events.*;
import flash.display.MovieClip;
import classes.VideoData;
public class PlayList extends MovieClip
{
private var vData:VideoData;
public function PlayList()
{
vData = new VideoData();
vData.addEventListener(Event.COMPLETE, onXMLCompleteHandler);
}
private function onXMLCompleteHandler(e:Event):void
{
vData.removeEventListener(Event.COMPLETE, onXMLCompleteHandler);
trace(vData.getXML());
}
}
}
package classes
{
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.display.MovieClip;
public class VideoData extends MovieClip
{
private var myXML:XML;
private var myXMList:XMLList;
private var myXMLLoader:URLLoader;
public function VideoData()
{
myXMLLoader = new URLLoader();
myXMLLoader.load(new URLRequest("playlist.xml"));
myXMLLoader.addEventListener(Event.COMPLETE, processXML);
}
private function processXML(e:Event):void
{
myXMLLoader.removeEventListener(Event.COMPLETE, processXML);
myXML = new XML(e.target.data);
myXMList = new XMLList(myXML.children());
dispatchEvent(e);
}
public function getXML():XML
{
return myXML;
}
}
}
You should also ALWAYS capitalise your class names 'VideoData' not 'videoData'

You will need for the XML data to have been received before getting a value.
Add a private Boolean _xmlLoaded in your videoData class , set it to true in the processXML method.
Create a getter
public function get xmlLoaded():Boolean
{
return _xmlLoaded;
}
Now you can do this:
private var data:videoData = new videoData();
private var xmlData:XML;
private function init():void
{
addEventListener(Event.ENTER_FRAME , xmlLoaded );
}
private function xmlLoaded(event:Event):void
{
if( videoData.xmlLoaded )
{
xmlData = videoData.getXML();
removeEventListener(Event.ENTER_FRAME , xmlLoaded );
}
}

You are already getting your private myXML variable out through the .getXML() method. This is the best way to expose encapsulated data to outside classes.
An alternative would be to make your myXML field public instead of private, but using the get/set accessor methods you are hiding your actual implementation from outside world.
[Edit]
If your getXML() method is returning null, it means that your event handler (the processXML method) has not yet been called.
The problem appears to be in your VideoData constructor:
public function videoData()
{
var myXMLLoader:URLLoader = new URLLoader();
myXMLLoader.load(new URLRequest("playlist.xml"));
myXMLLoader.addEventListener(Event.COMPLETE, processXML);
}
The XML file is probably loaded before you attach the event handler, and that is why the event fires right before you start to listen to it. Try to reverse it and see if it works:
public function videoData()
{
var myXMLLoader:URLLoader = new URLLoader();
myXMLLoader.addEventListener(Event.COMPLETE, processXML); // moved up
myXMLLoader.load(new URLRequest("playlist.xml"));
}

Related

Where must be custom logic in pureMVC (as3)?

I tried to write small as3 program based on framework pureMVC.
I understood basic principles of it, but I can't understand, where I must place custom logic.
For example, I must load 10 images. I created command, that init Proxy.
package app.controller
{
import app.model.GalleryProxy;
import dicts.Constants;
import org.puremvc.interfaces.INotification;
public class LoadFilesCommand extends BaseCommand
{
public function LoadFilesCommand() { }
override public function execute(note:INotification):void
{
facade.registerProxy(new GalleryProxy(Constants.FILES_LIST));
}
}
}
And Proxy is:
package app.model
{
import dicts.Constants;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.net.URLRequest;
import org.puremvc.interfaces.IProxy;
import org.puremvc.patterns.proxy.Proxy;
public class GalleryProxy extends Proxy implements IProxy
{
public function GalleryProxy(list:Vector.<String>)
{
super(Constants.PROXY_GALLERY);
_fileList = list;
_total = _fileList.length;
load();
}
public function get currentImage():Bitmap
{
return _images[_index];
}
//--------------------------------------------------------------------------
// PRIVATE SECTION
//--------------------------------------------------------------------------
private var _fileList:Vector.<String>;
private var _total:uint;
private var _loaded:uint = 0;
private var _images:Array = [];
private var _index:int;
private function load():void
{
var loader:Loader;
for (var i:int = 0; i < _total; i++)
{
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoadHandler);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
loader.load(new URLRequest(_fileList[i]));
}
}
private function imageLoadHandler(event:Event):void
{
var info:LoaderInfo = LoaderInfo(event.currentTarget);
_images[Constants.FILES_LIST.indexOf(info.url)] = info.content;
info.removeEventListener(Event.COMPLETE, imageLoadHandler);
info.removeEventListener(IOErrorEvent.IO_ERROR, errorHandler);
_loaded++;
if (_loaded >= _total)
sendNotification(Constants.COMMAND_SHOW_MAIN);
}
private function errorHandler(event:ErrorEvent):void
{
throw new Error("bad link or internet disconnect");
}
}
}
Now my Proxy is loading images independently (functions load() and imageLoadHandler)
Is it correct?
Or I must move this logic to Command class?
Or I must create some LoadService.as, which will contains this logic?
What is the correct variant for pureMVC?
Do you want to load your 10 images on application startup? If not, make load() public and call it from a Mediator, responding to a UI event.
If so, what you have will work fine. One alternative would be writing GalleryProxy so it doesn't call load() in the constructor - instead, you could have the Command register the proxy, load the image list, and call proxy.load(images[i]) in a loop.

as3 StageWebView in a class not displaying

I am sure this is an easy one. I have one Main.as class calling a another class that is loading a StageWebView. If called by itself the StageWebView works fine, but when I call it from another class it will not display. What simple thing am I forgetting?
Perhaps it has something to do with the "stage" in the loaded class?
Main.as
public function addPopeNews()
{
thePopeNews = new popeNews();
addChild(thePopeNews);
}
PopeNews.as
package com
{
import flash.display.MovieClip;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.*;
import flash.net.URLRequest;
import flash.media.StageWebView;
import flash.geom.Rectangle;
public class popeNews extends MovieClip
{
public var backBar:popeNewsBar;
public var webView:StageWebView;
public function popeNews()
{
webView=new StageWebView();
webView.stage = this.stage;
webView.loadURL("www.myUrl.com");
trace("POPE NEWS!!!"); /// trace works!
backBar = new popeNewsBar();
backBar.width = Main._screenX;
backBar.scaleY = backBar.scaleX;
webView.addEventListener(Event.COMPLETE, webLoaded);
webView.addEventListener(LocationChangeEvent.LOCATION_CHANGING,onChanging);
}
public function webLoaded(e:Event)
{
trace("web loaded"); // trace works!!
if (webView.isHistoryBackEnabled)
{
addChild(backBar);
backBar.bb.addEventListener(MouseEvent.CLICK, goBack);
webView.viewPort = new Rectangle(0,backBar.height,Main._screenX,Main._screenY - backBar.height);
}
else
{
webView.viewPort = new Rectangle(0,0,Main._screenX,Main._screenY);
}
}
public function goBack(e:Event)
{
if (webView.isHistoryBackEnabled)
{
trace("Called GO BACK");
webView.historyBack();
removeChild(backBar);
backBar.bb.removeEventListener(MouseEvent.CLICK, goBack);
return;
}
if (webView.isHistoryForwardEnabled)
{
webView.historyForward();
return;
}
}
public function onError(e:ErrorEvent):void
{
//infoBox.text="Page is not available. Try reloading.";
}
public function onChanging(e:LocationChangeEvent):void
{
//webView.viewPort = null;
trace("Called CHANGING!!!");
}
///
}
}
You are right, the stage is null in the PopeNews constructor. You should put your initialization code into a new method, and listen for the ADDED_TO_STAGE event.
public function popeNews()
{
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
private function addedToStageHandler(ev:Event):void
{
webView=new StageWebView();
webView.stage = this.stage;
webView.loadURL("www.myUrl.com");
trace("POPE NEWS!!!"); /// trace works!
backBar = new popeNewsBar();
backBar.width = Main._screenX;
backBar.scaleY = backBar.scaleX;
webView.addEventListener(Event.COMPLETE, webLoaded);
webView.addEventListener(LocationChangeEvent.LOCATION_CHANGING,onChanging);
}
Also, by convention class names are capitalized.

Using stage.addEventListener inside a class is returning a null object reference during runtime

I want to add an event listener to the stage from inside a class called "ChoiceBtn".
I get the error "1009: Cannot access a property or method of a null object reference". I understand that this is because the object is not yet instantiated.
Here is my code:
My main document code:
import ChoiceBtn;
var op1:ChoiceBtn = new ChoiceBtn("display meee", answer, 1, "a)", "4.jpg");
op1.x = 250;
op1.y = 60;
stage.addChild(op1);
My Class file:
package {
import AnswerEvent;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.SimpleButton;
import flash.events.*;
import flash.ui.Mouse;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.net.URLRequest;
import flash.display.Stage;
public class ChoiceBtn extends Sprite{
public var path:String;
public var choiceText:String;
public var choiceLabel:String;
private var answer:Answer;
private var choiceNum:uint;
private var textFormat:TextFormat = new TextFormat();
private var choiceLabelHwd:TextField = new TextField();
private var choiceTextHwd:TextField = new TextField();
private var boundingRect:Sprite = new Sprite;
private var hitAreaWidth = 255;
private var hitAreaHeight = 45;
private var pic:Loader = new Loader;
public function ChoiceBtn(choiceText:String, answer:Answer, choiceNum:uint, choiceLabel:String = "a)", picPath:String = null) {
//path - must be the path to a picture
//choiceText - the text to be displayed
//choiceLabel - the prefix selector such as answers '1' or 'a)' etc.
// constructor code
this.answer = answer;
this.choiceNum = choiceNum;
this.choiceLabel = choiceLabel;
this.choiceText = choiceText;
//add childs
addChild(this.choiceTextHwd);
addChild(this.choiceLabelHwd);
addChild(this.boundingRect); //must be added last so is on top of everything else
//add Listeners
//stage.addEventListener(AnswerEvent.EVENT_ANSWERED, update); //doesn't work
stage.addEventListener(AnswerEvent.EVENT_ANSWERED, this.update); //doesn't work either
}
public function update(e:Event):void {
trace("in choice fired");
}
}
}
I don't understand why it doesn't work even when I use this before the function. How can I create the eventlistener on the stage in this classes constructor code and reference a function inside this class.
Wait for the ADDED_TO_STAGE event to fire first:
public function ChoiceButton():void
{
// your code.. etc..
addEventListener(Event.ADDED_TO_STAGE,addListeners);
}
private function addListeners(event:Event):void
{
stage.addEventListener(AnswerEvent.EVENT_ANSWERED, update);
}

Loading flex Modules from an XML file

I am trying to parse an xml file and load Flex modules dynamically in my application. But it loads only the last module everytime. I have a singleton class which does the parsing and loading of modules. Here is the class
package
{
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
import mx.controls.Alert;
import mx.events.ModuleEvent;
import mx.modules.IModuleInfo;
import mx.modules.ModuleLoader;
import mx.modules.ModuleManager;
public class VappModuleManager
{
private static var _instance:VappModuleManager;
private static const MODULE_PATH:String="./com/emc/vapp/";
private static const MANIFEST_PATH:String="module-manifest.xml";
private var _module:IModuleInfo;
private var _handler:Function;
private var loader:URLLoader;
public function VappModuleManager(object:SingletonEnforcer)
{
}
public function set handler(handler:Function):void
{
_handler=handler;
}
public static function get instance():VappModuleManager
{
if(_instance==null)
{
_instance=new VappModuleManager(new SingletonEnforcer());
}
return _instance;
}
public function load():void
{
loader = new URLLoader();
loader.addEventListener(Event.COMPLETE, xmlLoaded);
loader.load(new URLRequest(MANIFEST_PATH));
}
private function xmlLoaded(event:Event):void
{
Alert.show("Event Completed");
var manifest:XML=new XML(event.target.data);
Alert.show(manifest.module.length());
for (var index:int=0;index<manifest.module.length();index++)
{
Alert.show(MODULE_PATH+manifest.module[index].#name);
_module=ModuleManager.getModule(MODULE_PATH+manifest.module[index].#name);
_module.addEventListener(ModuleEvent.READY,_handler);
_module.load();
}
}
}
}
internal class SingletonEnforcer {}
I use the above class as follows.
moduleManager=VappModuleManager.instance;
moduleManager.handler=myhandler;
moduleManager.load();
I understand the problem is with eventlistener for variable "_module" but dont know how to solve it. Any help appreciated.
The call to IModuleInfo.load is asynchronous so your for loop has run completely before any of the modules have loaded. Also, your class level _module property is overwritten by a new Module instance each time the loop iterates.
I'd suggest loading each module sequentially by waiting for the READY event and initiating the load of the next module only when it has fired. I'd also fire an event when all the modules are loaded instead of executing a callback function as this will give you more flexibility (multiple objects can listen for an event for example).
The following isn't tested, but should give you the idea:
package
{
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
import mx.controls.Alert;
import mx.events.ModuleEvent;
import mx.modules.IModuleInfo;
import mx.modules.ModuleLoader;
import mx.modules.ModuleManager;
public class VappModuleManager
{
private static var _instance:VappModuleManager;
private static const MODULE_PATH:String="./com/emc/vapp/";
private static const MANIFEST_PATH:String="module-manifest.xml";
private var loader:URLLoader;
private var manifest:XML;
private var loadCount:int = 0; // track loaded modules
public function VappModuleManager(object:SingletonEnforcer)
{
}
public static function get instance():VappModuleManager
{
if(_instance==null)
{
_instance=new VappModuleManager(new SingletonEnforcer());
}
return _instance;
}
public function load():void
{
loader = new URLLoader();
loader.addEventListener(Event.COMPLETE, xmlLoaded);
loader.load(new URLRequest(MANIFEST_PATH));
}
private function xmlLoaded(event:Event):void
{
manifest =new XML(event.target.data);
// load the first module
loadModule();
}
private function loadModule() {
// Use a locally scoped variable to avoid writing over the previous instance each time.
// You could push these onto an array if you need to be able to access them later
var module:IModuleInfo = ModuleManager.getModule(MODULE_PATH+manifest.module[loadCount].#name);
module.addEventListener(ModuleEvent.READY, moduleReadyHandler);
module.load();
}
private function moduleReadyHandler(event:ModuleEvent) {
// Remove the event listener on the loaded module
IModuleInfo(event.target).removeEventListener(ModuleEvent.READY, moduleReadyHandler);
loadCount ++;
// Are there still modules in the manifest to load?
if (loadCount < manifest.module.length()) {
// Yes... load the next module
loadModule();
} else {
// No... we're all finished so dispatch an event to let subscribers know
dispatchEvent(new Event("complete"));
}
}
}
}

Flex 4.5 Not listening to custom event dispatched in custom AS class

I have the following situation:
I have an event handler, that displays small messages in my application's statusbar.
These messages get passes through by dispatching events from custom components.
A simple message could be like "HTTP Error" or so.
Now, the main event listener, in the main application file, listens to the event dispatched by any custom component, but seems to refuse listening to events dispatched by custom AS classes.
Here is my code for the custom event:
package main.events
{
import flash.events.Event;
public class ShowNoticeEvent extends Event
{
public var message:String;
public static const SHOW_NOTICE:String = "showNotice";
public function ShowNoticeEvent(type:String, msg:String, bubbles:Boolean = false, cancelable:Boolean = false)
{
super(type, bubbles, cancelable);
this.message = msg;
}
override public function clone():Event
{
return new ShowNoticeEvent(type, message);
}
}
}
This is the event listener in the main application file:
addEventListener(ShowNoticeEvent.SHOW_NOTICE, showNoticeListener, true);
And this is the custom AS class that dispatches the custom event. I pasted all the code, so you could see the whole part of it.
package components.dashboard
{
import components.dashboard.models.*;
/* Event imports */
import flash.events.*;
import main.events.*;
import mx.controls.Alert;
import mx.core.UIComponent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
[Event(name="showNotice", type="main.events.ShowNoticeEvent")]
public class Controller extends UIComponent
{
private var baseUrl:String;
public function Controller(baseUrl:String)
{
this.baseUrl = baseUrl;
}
public function getRunningQuotations():void
{
var runningQuotationsList:RunningQuotationsList = RunningQuotationsList.getInstance();
execService("index.php?a=1", runningQuotationsList.updateList, "pnlRunningQuotations");
}
public function getRecentProjects():void
{
var recentProjectsList:RecentProjectsList = RecentProjectsList.getInstance();
execService("index.php?a=2", recentProjectsList.updateList, "pnlRecentProjects");
}
public function getLatestCustomers():void
{
var latestCustomersList:LatestCustomersList = LatestCustomersList.getInstance();
execService("index.php?a=3", latestCustomersList.updateList, "pnlLatestCustomers");
}
private function execService(url:String, listener:Function, component:String):void
{
var basicService:HTTPService = new HTTPService(baseUrl);
basicService.showBusyCursor = true;
basicService.addEventListener(FaultEvent.FAULT, function(e:FaultEvent):void{httpFault(e, component)});
basicService.method = "POST";
basicService.resultFormat = "text";
basicService.url = url;
basicService.addEventListener(ResultEvent.RESULT, listener);
basicService.send();
}
private function httpFault(event:FaultEvent, component:String = null):void {
var faultstring:String = event.fault.faultString;
var eventObj:ShowNoticeEvent = new ShowNoticeEvent(ShowNoticeEvent.SHOW_NOTICE, faultstring, true);
dispatchEvent(eventObj);
trace(faultstring);
}
}
}
So to sum it all up:
- The event listener listens to the custom event dispatched by any custom component.
- The event listener does not listen to the custom event duspatched by an AS class.
Those who wonder, the event really gets dispatched, that's why I added a trace call.
The instance of Controller Class would have to be added to stage for that to work.
by doing
addEventListener(ShowNoticeEvent.SHOW_NOTICE, showNoticeListener, true);
in the main file you are adding the listener to the stage.
So basically you are doing.
stage.addEventListener(ShowNoticeEvent.SHOW_NOTICE, showNoticeListener, true);
If controler instance is not on stage you won't see the event.
You might want to look into a Singleton type pattern for your data management as that would fit this setup pretty good.
Main:
Controller.getLastInstance().addEventListener(ShowNoticeEvent.SHOW_NOTICE, showNoticeListener, true)
.
package components.dashboard
{
import components.dashboard.models.*;
/* Event imports */
import flash.events.*;
import main.events.*;
import mx.controls.Alert;
import mx.core.UIComponent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
[Event(name="showNotice", type="main.events.ShowNoticeEvent")]
public class Controller extends UIComponent
{
static public function getLastInstance():Controller { return _instance; }
static private var _instance:Controller;
private var baseUrl:String;
public function Controller(baseUrl:String)
{
_instance = this;
this.baseUrl = baseUrl;
}
public function getRunningQuotations():void
{
var runningQuotationsList:RunningQuotationsList = RunningQuotationsList.getInstance();
execService("index.php?a=1", runningQuotationsList.updateList, "pnlRunningQuotations");
}
public function getRecentProjects():void
{
var recentProjectsList:RecentProjectsList = RecentProjectsList.getInstance();
execService("index.php?a=2", recentProjectsList.updateList, "pnlRecentProjects");
}
public function getLatestCustomers():void
{
var latestCustomersList:LatestCustomersList = LatestCustomersList.getInstance();
execService("index.php?a=3", latestCustomersList.updateList, "pnlLatestCustomers");
}
private function execService(url:String, listener:Function, component:String):void
{
var basicService:HTTPService = new HTTPService(baseUrl);
basicService.showBusyCursor = true;
basicService.addEventListener(FaultEvent.FAULT, function(e:FaultEvent):void{httpFault(e, component)});
basicService.method = "POST";
basicService.resultFormat = "text";
basicService.url = url;
basicService.addEventListener(ResultEvent.RESULT, listener);
basicService.send();
}
private function httpFault(event:FaultEvent, component:String = null):void {
var faultstring:String = event.fault.faultString;
var eventObj:ShowNoticeEvent = new ShowNoticeEvent(ShowNoticeEvent.SHOW_NOTICE, faultstring, true);
dispatchEvent(eventObj);
trace(faultstring);
}
}
}
Not Ideal since you could only ever have 1 of them.
But I think better than having to turn a simple EventDispatcher into DisplayObject and add it to stage just to Simply bubble.