How to Make Popup window as Global in Flex? - actionscript-3

Actually in my Flex Application have Some Popup windows and i want take some values in this Popup Window But The Values are Comming NULL
So how to Make a PopUp Window as Global? Because we are Using the values Globally.
Please Give Suggestion...
Edit
I'm Edit with some code...
Main.mxml(Main Aplication), Demo1.mxml(PopUpWindow), Demo2.mxml(PopUpWindow)
Now in Demo1.mxml have variable like...
[Bindable]private var arrayC:ArrayCollection=new ArrayCollection();//Hear Some value their.
NOw i want Use arrayC in Demo2.mxml then ..
public var variable1:Demo1=new Demo1();
var ac:ArrayCollection = new ArrayCollection();
ac = variable1.arrayC;
But hear ac contain Null Value Why?
Then,Now i'm Thinking Demo2.mxml(PopUpWindow) is Converting To Global Scope so it's value Used in Any Where .

Null because of you are tried create new instance so that each instance having their own state.
Also i bet you can't access arrayC ArrayCollection variable declared as private so you can't acccess.
Need to follow few steps
[Bindable]public var arrayC:ArrayCollection=new ArrayCollection(); //Make public variable
Use Singleton Class for you application
package com.foo.bar {
public class Model {
private static var instance : Model;
public function Model( enforcer : SingletonEnforcer ) {}
public static function getInstance() : Model {
if (!instance) {
instance = new Model( new SingletonEnforcer() );
}
return instance;
}
public var arrayC:ArrayCollection = new ArrayCollection();
}
}
class SingletonEnforcer{}
For more details Singleton pattern
private var popup:Demo1;
popup = PopUpManager.createPopUp(this,Demo1,true) as Demo1;
popup.arrayC = Model.getInstance().arrayC; // Here set value from Model class
PopUpManager.centerPopUp(popup);
Suppose you tried to access demo1.mxml arrayC variable in Demo2.mxml
var demo1_arrayC = Model.getInstance().arrayC;
Note that you can access arrayC arraycollection anywhere in your application like Demo2.mxml,Demo3...etc.
But better we have to avoid Singleton Class (unit test diffcult ..etc).

If you are using the values Globally, then no matter what you mean by the "Make a PopUp Window as Global", I strongly suspect that you would be best served by a singleton event dispatcher.
package com.example.demo.models {
import flash.events.IEventDispatcher;
import flash.events.EventDispatcher;
[Bindable]
class MyGlobalStuff extends EventDispatcher {
public var someGlobalValue:*;
private var _instance:MyGlobalStuff;
public function MyGlobalStuff (lock:SingletonLock, target:IEventDispatcher=null) {
super(target);
if(!(lock is SingletonLock)) {
throw(new Error("MyGlobalStuff is a singleton, please do not make foreign instances of it"));
}
}
public static function getInstance():MyGlobalStuff {
if(!_instance) {
_instance = new MyGlobalStuff (new SingletonLock());
}
return _instance;
}
}
}
class SingletonLock{}
The idea is this: that you would bind in your popup to
{myStuffModel.someGlobalValue}
myStuffModel would be initialized in your mxml as:
protected var myStuffModel:MyStuffModel = MyStuffModel.getInstance();
then in any other class throughout your application you can bind to or access the EXACT same data via the singleton model.

Related

Accessing a variable defined by a constructor function through another class

I have two classes, the first one is called CoreModus. In CoreModus (which is a non-document class) I declare a "global" variable, called modus, using the constructor function CoreModus(modus);
CoreModus.as:
package myStudio.basic {
public class CoreModus {
public var modus:String;
public function CoreModus(structure:String) {
modus = structure;
}
public function setup():String {
return modus;
}
}
}
The second class is called Animation, which is a non-document class, and I want to access the variable modus, which is declared in CoreModus constructor function: i.e CoreModus("non-linear"); which in this case modus = non-linear.
Animation.as:
package myStudio.basic {
import fl.transitions.Tween
public class Animation {
public var anim:Tween;
public function Animation() {}
public function tryToRetrieveModus():void {
var modo:CoreModus = new CoreModus();
var modus:String = modo.getModus();
trace("I'm trying to retrieve the modus " + modus);
}
}
}
Of course, because CoreModus needs a parameter, I can't use the option I tried in Animation.as (making an instance of CoreModus).
FLA document, frame 1:
import myStudio.basic.CoreModus;
import myStudio.basic.Animation;
var modo:CoreModus = new CoreModus("non-linear");
var mov1:Animation = new Animation();
trace(modo.setup());
mov1.tryToRetrieveModus();
Is there any other way to access to this variable?
P.S. I omitted a bunch of unrelated lines in CoreModus(); constructor function. I don't want that code to be processed every time, for CPU's sake.
To access the variable from your CoreModus class instance, first create the instance of the class somewhere.
Then, pass the instance reference to the the Animation class instance. After that you can use the public variable of CoreModus as you please.
Here's an example:
//creating the CoreModus class instance
var myCoreModus:CoreModus = new CoreModus("my string");
//creating the Animation class instance
var myAni:Animation = new Animation();
myAni.modo = myCoreModus; // make sure that modo is public instance variable
You can also make your CoreModus a singleton and have a static variable if you have only one instance of your CoreModus.

as3 declaring a GLOBAL variable - in TIMELINE / outside of CLASS

I am looking to declare a GLOBAL VAR in the main time line.
Then I need to access that GLOBAL VAR from another externally loaded SWF's.
QUESTIONS:
How do I create the global var in the main timeline?
How do I access that var in externally loaded swf files?
First, you shouldn't use any global/static state. In your situation this is even more true, because Singletons are a royal pain in the butt across different applicationDomains.
Instead, you should use something called Dependency Injection. Think of your little swfs as starving orphans. When they have loaded, they don't run up to your main swf and pick its pockets. Instead, the main swf magnanimously presses money into their little hands.
So, how do we make this happen? One way is that we could compile a reference to their Document class(es) into the main swf, and then we could set a variable that the Class exposes. However, this can get pretty heavy and isn't really necessary.
Instead, you can write something called an Interface, which defines the "idea" of an orphan.
It might look something like this:
public interface IOrphan {
function get alms():Number;
function set alms(value:Number):void;
}
Note that you have to use getters and setters with Interfaces, because you can't use them to define vanilla variables. However, that's going to work out great for our actual Orphan:
public class Oliver implements IOrphan {
private var _alms:Number;
private var _totalAlms:Number;
public var tf:TextField;//put this on stage and allow Flash to populate automatically
public function get alms():Number {
return _alms;
}
public function set alms (value:Number):void {
_alms = value;
_totalAlms += _alms;
updateAlmsMessage();
}
private function updateAlmsMessage():void {
tf.text = 'That was a donation of ' + _alms + '.\n'
'I now have ' _totalAlms + '.\n'
'Please, sir, can I have some more?';
}
}
Now, all you need to do is populate that variable on load. There are several ways you can do this, such as watching the stage for IOlivers to be loaded, or you could be more direct about it:
private function loadSwf(url:String):void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
var request:URLRequest = new URLRequest(url);
loader.load(request);
addChild(loader);
}
private function completeHandler(e:Event):void {
((e.target as LoaderInfo).content as IOrphan).alms = .25;
}
If these are variables that you only want to set once and will never change, you can just create a class that holds static constants.
package
{
public class Env
{
public static const WHATEVER:String = "Whatever!";
public function Env()
{}
}
}
Then you could access them later in your program like so:
trace(Env.WHATEVER);
However, if you want global variables that can change, I like to handle this by using a singleton class.
package
{
import flash.events.EventDispatcher;
public class Control extends EventDispatcher
{
//---------------------------------------
// PRIVATE & PROTECTED INSTANCE VARIABLES
//---------------------------------------
private static var _instance:Control;
//---------------------------------------
// PUBLIC VARIABLES
//---------------------------------------
public var whatever:String = "Whatever";
//---------------------------------------
// PUBLIC METHODS
//---------------------------------------
public static function get instance():Control
{
return initialize();
}
public static function initialize():Control
{
if (_instance == null)
{
_instance = new Control();
}
return _instance;
}
//---------------------------------------
// CONSTRUCTOR
//---------------------------------------
public function Control()
{
super();
if (_instance != null)
{
throw new Error("Error:Control already initialised.");
}
if (_instance == null)
{
_instance = this;
}
}
}
}
The difference here is that you need to grab the instance of your singleton before you can get to what's inside it. It'd look a little bit like this.
private var _control:Control = Control.instance;
// Reading a global variable
trace(_control.whatever);
// Change a global variable
_control.whatever = "Foobar!";
So whenever you change "whatever", that variable will change for all loaded SWFs. If you want to be really fancy about it, you could use getters/setters in your singleton rather than simple public variables.

Cleaning ArrayCollections

Initially I had ten arraycollections declared in a flex module, which I thought, were causing a memory leak. So I separated them in a single class, which I would clean using the "destroy" method I created inside it. Would this work?
I hate the question title excuse me. But I wouldn't write it like "Seducing the garbage collector"
[Bindable]
public class Cfd
{
private static var instance:Cfd = new Cfd();
private var _cfds:ArrayCollection = new ArrayCollection();
// Constructor
public function Cfd(){
if (instance) { throw new Error('Cannot create a new instance. Must use Cfd.getInstance().') }
}
public static function getInstance():Cfd{
return instance;
}
public function get cfds():ArrayCollection{
return _cfds;
}
public function set cfds(value:ArrayCollection):void{
_cfds = value;
}
public function destroy():void{
if(_cfds != null){
_cfds.removeAll();
}
}
}
Whenever you use Singletons like this, you pretty much guarantee a memory leak, because you're probably listening to the ArrayCollection (and maybe items within it) from all over the place. When you explicitly provide a reference to an object through a getter/setter pair, you can add the listener in the setter and removie it when the value is reset.
Check out http://www.developria.com/2010/08/rethinking-addeventlistener-an.html for more on what's happening.
For more on why you should avoid Singletons http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars
Just null the object should do it unless you have listeners attached to it.
cfds = null;
I have never seen [Bindable] used on the class itself, so not sure what you are trying to do there.
package{
public final class Cfd{
private static var instance;
private var _cfds:ArrayCollection = new ArrayCollection();
public function Cfd(singletonEnforcer:MySingletonEnforcer){
if (instance) { throw new Error('Cannot create a new instance. Must use Cfd.getInstance().') }
}
public static function getInstance():Cfd{
if (instance == null)
instance = new MySingleton(new MySingletonEnforcer());
return instance;
}
// don't see a real need for setter/getters here
public static function get cfds():ArrayCollection{
return _cfds;
}
public static function set cfds(value:ArrayCollection):void{
_cfds = value;
}
// I wouldn't even use a destroy on this class since it is a singleton.
// just set the data to null Cfd.cfds = null
public static function destroy():void{
_cfds = null
}
}
}
//this is in Cfd.as but is outside the package block
class MySingletonEnforcer {}

How do I make my public static logging method write to an inputfield?

I figured out how to create a static method that is available everywhere, for example:
UtilLib.as:
package
{
public final class UtilLib
{
public static function getTimeStamp():uint
{
var now:Date = new Date();
return now.getTime();
}
}
}
I can access this everywhere by doing UtilLib.getTimeStamp() - Now, I want to create a new staic method called log(msg:String). This should log a message to a multi-line inputfield.
The problem however is that this inputfield must be created somewhere and must be accessible and visible all the time, and I don't want to pass it through the function parameters all the time as this would cause a lot of trouble (I'd have to pass it through objects aswell..).
So, how do I make a "public textfield" so my static log method can write to it?
UPDATE:
I now tried the following class, with a static constructor (I think). However, the textfield object is not showing. When I do an addChild(debugField) after creating it, it gives me error 1180.
Logger.as
package
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFieldType;
public class Logger extends Sprite
{
public static var debugField:TextField;
/* static block */
{
trace("Logger initializing.");
debugField = new TextField();
debugField.width = 500;
debugField.height = 100;
debugField.x = 100;
debugField.y = 400;
debugField.background = true;
debugField.backgroundColor = 0xFFFFFF;
debugField.defaultTextFormat = new CustomTextFormat();
debugField.mouseWheelEnabled = true;
debugField.multiline = true;
debugField.type = TextFieldType.DYNAMIC;
}
public static function log(msg:String):void
{
if (debugField) debugField.appendText(msg);
}
}
}
I initialize it like this:
var test:Logger = new Logger();
addChild(test);
And I log a new message like this:
Logger.log("test");
Unfortunately, the textField is not showing.
Essentially you need:
somewhere to log a message which is globally accessible
the ability to update a text field whenever the log message changes
A simple solution using objects could look like this:
Example
package {
import flash.display.Sprite;
import flash.text.TextField;
import flash.events.Event;
public class Example extends Sprite {
private var messageLog:TextField;
public function Example() {
createTextField();
MessageLogger.getInstance().addEventListener( MessageLogger.LOG, handleMessageLoggerUpdate );
MessageLogger.getInstance().log( "Message!" );
}
private function handleMessageLoggerUpdate( event:Event ):void {
messageLog.text = MessageLogger.getInstance().getLog();
}
private function createTextField():void {
messageLog = new TextField();
addChild( messageLog );
}
}
}
MessageLogger
package {
import flash.events.EventDispatcher;
import flash.events.Event;
public class MessageLogger extends EventDispatcher {
private static var instance:MessageLogger;
public static function getInstance():MessageLogger {
if ( !instance ) {
instance = new MessageLogger( new InstanceKey() );
}
return instance;
}
public static var LOG:String = "MessageLoader#log";
private var messageLog:String;
public function MessageLogger(key:InstanceKey) {
messageLog = "";
}
public function log( message:String ):void {
messageLog += message;
notify();
}
public function getLog():String {
return messageLog;
}
private function notify():void {
dispatchEvent( new Event( LOG ) );
}
}
}
class InstanceKey {}
The important thing here is that a message can be logged from anywhere using
MessageLogger.getInstance().log( "Your Message Here" );
and anything can be notified of when a message has been logged using
MessageLogger.getInstance().addEventListener( MessageLogger.LOG, listenerFunction );
at any point the current message log can be obtained using
MessageLogger.getInstance().getLog();
Create a new Logging class and have that class have a static constructor. Add your logging method to this class. Make the static constructor save the logging field to a private variable. Now before you call the logging method just call your static constructor with the input field you'd like to use. This will create the class, set up the input field as the destination, and now you can simply just call the log function from anywhere.
Traditionally, the way you let static methods interact with private variables is to pass them in. Pass in a pointer to your textbox.
so instead of
public static function getTimeStamp():uint { ... }
you have
public static function writeTimeStamp(messageBox):uint { ... }
The syntax might be incorrect as I'm not an AS dev but, do you see what I mean? From there, that block of code can access messageBox as if it were a local variable. Well it is.
(I renamed the method name for clarity. You could even stop it returning a variable if it doesn't need to but you'll need to change the declaration further.)
In your updated post the debug text field needs to be added to the display list somewhere. Right now it looks like it is just created and used during that log method. You add display objects to the display list by calling addChild( displayObject:DisplayObject ):Boolean; on a DisplayObjectContainer that is already a child of stage.

custom AS3 Loader class that uses a custom LoaderInfo class?

This is a question specifically about creating a custom Loader class as a followup to How do I modify existing AS3 events so that I can pass data?. If this wasn't the right way to go about this, let me know.
I have a custom Loader class and a custom LoaderInfo class. What I can't figure out is how to have the Loader class use the custom LoaderInfo class as it's contentLoaderInfo property.
package com.display {
import flash.display.Loader;
import com.display.CustomLoaderInfo;
public class CustomLoader extends Loader {
public var customData:*;
public function CustomLoader(passedData: *= null) {
customData = passedData;
}
}
}
^ Have to do something in there to make it have the new CustomLoaderInfo
package com.display {
import flash.display.LoaderInfo;
import flash.events.Event;
import com.events.CustomEvent;
public class CustomLoaderInfo extends LoaderInfo {
public var customData:*;
public function CustomLoaderInfo(passedData: *= null) {
customData = passedData;
}
override public function dispatchEvent(event:Event):Boolean {
var customEvent:CustomEvent = new CustomEvent(event.type, customData, event.bubbles, event.cancelable);
return super.dispatchEvent(customEvent);
}
}
}
^ That might work, but since I can't get CustomLoader to use it, I don't know yet.
I don't know how it fits into what you're doing, but you could not bother making an extended LoaderInfo at all. Loader.contentLoaderInfo has a property called loader that will return the Loader (or in this case CustomLoader) that was used. In theory you can just do this with the CustomLoader class that you have:
var loader: CustomLoader = new CustomLoader("Extra Data");
loader.load(new URLRequest("file.swf"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderComplete);
function loaderComplete(event: Event) : void
{
var customData:* = (event.target.loader as CustomLoader).customData;
trace(customData); // Extra Data
}
You would have to override the function where the loaderinfo instance is created, and that might be in a private function. In the new function you could then instanciate your custom loaderInfo class instead, but you would also have to do anything else that the overwritten method does. You might run into some security sandbox problems with loadinfo as well. Overwriting a function like this is usually only posible when the function written with this in mind.
Alternatively you could use the prototype chain (read here) to change the loaderInfo class at runtime. But I wouldn't recommend it. It's not good practice in my opinion.
If you get this to work you still have the problem that your custom event will not work with any event that has properties not inheritet from the Event class (like the ProgressEvent that has progress related properties).
I would say you properly should look for another solution.
In the question you link to you talk about sending movieclip with your event. Have you thought about reaching the other way? When you recieve the event, you get a reference to the object that dispatched it (Event.target). Can you not use that reference to get the movieclip?
Perhaps you could explain your problem in more detail (maybe in a new question so you still keep this open), I am sure there is a better / easier way to solve it.
I needed something like this because I wanted to carry an index with the info ... ie - have something like event.target.index available after an event is triggered .. anyway i designed a class that contained the loader and the loader info... heres the class
public class LoaderBox extends Sprite {
public static var PROGRESS:String = "progress"
public static var COMPLETE:String = "complete"
public var id:int
public var index:int
public var loader:Loader
public var info:LoaderInfo
public var isOpen:Boolean
//
public var percent:Number = -1
public function load(path:String,idx:int=-1,nm:String=null){
loader = new Loader()
if (nm != null){
name = nm
}
index = idx
var req:URLRequest = new URLRequest(path)
info = loader.contentLoaderInfo
info.addEventListener(Event.COMPLETE,complete)
info.addEventListener(ProgressEvent.PROGRESS,progress)
isOpen = true
loader.load(req)
}
private function complete(e:Event):void{
isOpen = false
dispatchEvent(new Event(LoaderBox.COMPLETE))
}
private function progress(e:ProgressEvent):void{
if (e.target.bytesTotal>0){
percent = Math.floor(e.target.bytesLoaded/e.target.bytesTotal * 100)
}
dispatchEvent(new Event(LoaderBox.PROGRESS))
}
}
A potential issue with the flagged answer: if contentLoaderInfo spits out a IOError, you can't access the .loader property to access your custom loader class.
What I did is the following:
* in my custom loader class, create the following method:
public function requestError(event:Event):void {
dispatchEvent(event);
}
when adding a listener to contentLoaderInfo for the IOerror, point to the custom loader class's method:
_loaderCls.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, _loaderCls.requestError);
Then, add the same listener to your loader class and make it call any method you need. At that point, the event.target would be that of your custom loader class and you can add any extra info you need to that class:
_loaderCls.addEventListener(IOErrorEvent.IO_ERROR, requestError);
or you can do a better thing to pass data into the Loader class:
package com.display
{
import flash.display.Loader;
public class Loader extends flash.display.Loader
{
private var _obj:Object;
public function Loader():void
{
super();
}
public function get obj():Object
{
return _obj;
}
public function set obj(o:Object):void
{
_obj = o;
}
}
}