AS3 - Initialising Objects Dynamically - actionscript-3

I've been trying to improve the code I use often, and was wondering if there was an easy way to achieve this.
I have a navigation class that defines the objects prior to the constructor (private var exampleScreen:ExampleScreen;).
Based on a string dependency injection for a ChangeScreenTo function (destinationScreen: String), is it possible to grab the defined screen and initialise it (exampleScreen = new ExampleScreen();) dynamically?
Either way, does anyone have any better suggestions than this or my current solution of a switch statement?
Example Code:
package{
public class ScreenController extends MovieClip {
private var currentScreen: DisplayObject;
//SCREENS
private var exampleScreen1:ExampleScreen1;
private var exampleScreen2:ExampleScreen2;
...
public function ScreenController() {
...
}
public function ChangeScreenTo(desinationScreenName: String) {
//REMOVE CURRENT SCREEN
if (currentScreen != null) {
removeChild(currentScreen);
currentScreen = null;
}
switch (destinationScreenName) {
case "exampleScreen1":
exampleScreen1 = new ExampleScreen1();
break;
case "exampleScreen2":
exampleScreen2 = new ExampleScreen2();
break;
...
}
mcDestinationScreen = this[(destinationScreenName)];
addChild(mcDestinationScreen);
currentScreen = mcDestinationScreen;
}
}
}

May be this is better:
public function ChangeScreenTo(desinationScreenName: String) {
//REMOVE CURRENT SCREEN
if (currentScreen != null) {
removeChild(currentScreen);
currentScreen = null;
}
switch (destinationScreenName) {
case "exampleScreen1":
currentScreen = exampleScreen1 = new ExampleScreen1();
break;
case "exampleScreen2":
currentScreen = exampleScreen2 = new ExampleScreen2();
break;
...
}
addChild(currentScreen);
}

Related

Can't call method after casting

I'm trying to figure out why ActionScript won't let me call methods on an Object after casting it.
I have a set of objects in an Array, all of which extend my ActionBase class. I want to go through all of them and call their step() and done() methods.
However, when I try to do so, the compiler gives me an error:
Error: Call to a possibly undefined method
If I simply create an ActionBase Object directly and call these methods on it, it works fine. So I suspect there's something about the casting that ActionScript doesn't like.
This is the code:
private var actionQueue:Array = new Array();
...
var action:ActionBase;
for (var i:int = 0; i < actionQueue.length; ++i)
{
action = actionQueue[i] as ActionBase;
if (action != null && action is ActionBase)
{
action.step();
if ( action.done() )
{
newQueue.push(action);
}
}
}
actionQueue is being appended to by having it's push() method called with objects which extends ActionBase. I'm also using FlashDevelop as my IDE. Not sure if that makes a difference.
My ActionBase class:
public class ActionBase
{
public function ActionBase()
{
}
public function done():Boolean
{
return true;
}
public function step():void
{
}
}
It may be as easy as changing the method from private to public. (didn't try it myself though and sorry can't write comments due to reputation level.)
Your example code looks fine, what does your ActionBase class and Array (actionQueue) assignment look like?
i.e.
actionQueue[actionQueue.length] = new ActionBase();
Cut/Paste Example:
package {
import flash.display.Sprite;
public class Main extends Sprite {
private var actionQueue:Array = new Array();
public function Main() {
var action:ActionBase;
var foobar1:ActionBase = new ActionBase();
actionQueue[actionQueue.length] = foobar1;
actionQueue[actionQueue.length] = new ActionBase();
actionQueue[actionQueue.length] = Object;
for (var i:int = 0; i < actionQueue.length; ++i)
{
action = actionQueue[i] as ActionBase;
if (action != null && action is ActionBase)
{
action.step();
if (action.done())
{
trace("all done");
}
}
}
}
}
}
class ActionBase {
var _done:Boolean = false;
function ActionBase() {
}
function done():Boolean {
trace(done);
return _done;
}
function step() {
_done = true;
trace(step);
}
}
Is your object a movieclip?
Did you try setting ActionBase as base class for your objects?
Or trying casting like this:
ActionBase(Arr[i]).step()
Or just try without casting.

Flex - Problems in accessing static variable on another mxml page

First.mxml - Contains a Datefield control as follows:
<mx:DateField id="G2_CRTLoadDate" width="150" selectedDate="{modelProxy.G2_CRTLoadDate}" change="{modelProxy.G2_CRTLoadDate = event.currentTarget.selectedDate;changeManagerStatus()}"/>
I'm assigning this Datefield value to a static variable CERT_LOAD_DATE as follows(First.mxml):
[Bindable]
public static var CERT_LOAD_DATE:String = "";
private function changeManagerStatus():void
{
CERT_LOAD_DATE = G2_CRTLoadDate.selectedDate.toDateString();
}
Second.mxml -Here, I have a Combobox as follows:
<mx:ComboBox id="General_Release_Dates"
selectedItem="{modelProxy.General_Release_Dates}"
valueCommit="{model.General_Release_Dates = event.currentTarget.selectedItem;updateReleaseDate(event)}"
change="{model.General_Release_Dates = event.currentTarget.selectedItem;updateReleaseDate(event)}" close="closeHandler(event);" includeInLayout="true" visible="true">
</mx:ComboBox>
Inside the closeHandler function, I'm trying to access the variable CERT_LOAD_DATE as follows:
private function closeHandler(evt:DropdownEvent):void {
var CurrentDate:Date = new Date();
if(General_Release_Dates.selectedLabel.toString() == "TBD")
{
Alert.show(First.CERT_LOAD_DATE);
}
}
The alert box displays no value (null). Please help.
I can't figure out the relationship between First.mxml and Second.mxml from your question.
However, the following code can't access First.mxml.
Alert.show(First.CERT_LOAD_DATE);
Because the "First" is not the same instance as loaded "First.mxml".
How about to use singleton? It's accessible from anywhere.
1st, add MySingleton.as class like this.
package foo.bar
{
public class MySingleton
{
private var _cert_load_date:String;
public function MySingleton(internally:SingletonInternal)
{
super();
if(internally == null)
{
throw new Error("Please use getInstance() method.");
}
}
public static function getInstance():MySingleton
{
return SingletonInternal.instance;
}
public function set cert_load_date(value:String):void
{
_cert_load_date = value;
}
public function get cert_load_date():String
{
return _cert_load_date;
}
}
}
import foo.bar.MySingleton;
class SingletonInternal{
public static var instance:MySingleton
= new MySingleton(new SingletonInternal());
public function SingletonInternal(){}
}
How to use
Set value at First.mxml.
public var singleton: MySingleton = MySingleton.getInstance();
private function changeManagerStatus():void
{
singleton.cert_load_date = G2_CRTLoadDate.selectedDate.toDateString();
}
Second.mxml
public var singleton: MySingleton = MySingleton.getInstance();
private function closeHandler(evt:DropdownEvent):void {
var CurrentDate:Date = new Date();
if(General_Release_Dates.selectedLabel.toString() == "TBD")
{
Alert.show(singleton.cert_load_date);
}
}
Updated: Aug 27 10:00(JST)
I think there are two way to change First.mxml's element using singleton.
1) Binding the DateField value to singleton variables, and clear the value at Secend.mxml.
2) Assign to singleton variables whole "First", and control from Second.mxml.
I'll write here the 2nd way.
If you use this way, anything is controlable from Second.mxml.
MySingleton.as
private var _first:Object;
public function set first(value:Object):void
{
_first = value;
}
public function get first():Object
{
return _first;
}
First.mxml
singleton.first = this;
Second.mxml
public function something(): void{
First(singleton.first).G2_CRTLoadDate.selectedDate = null;
// The cast is unnecessary. Following code also works.
// singleton.first.G2_CRTLoadDate.selectedDate = null;
}
Also you can execute First.mxml's public function from Second.mxml.
singleton.first.someFunctionDefinedAtFirst();

use of addChild inside a function to add a child into a previously added instance

I wrote a simple function "adone" to add things to the stage and today I decided to make it add child to other objects as well but the test function "ad2" I made is not working right. what do you suggest?
package {
import flash.display.MovieClip;
public class main extends MovieClip {
public var ui:Array = new Array;
public var splz:Array = new Array;
public var pows:powsys = adone("powsz",powsys,ui,400,240);
public var s1:powsyspsl = ad2("s1",powsyspsl,splz,100,100,"powsz");
public function main() {
}
public function adone(nm,tp,lst,ex=0,ey=0) {
nm = new tp();
addChild(nm);
lst.push(nm);
nm.x = ex;
nm.y = ey;
}
public function ad2(nm,tp,lst,ex=0,ey=0,par=null) {
nm = new tp();
par.addChild(nm);
lst.push(nm);
nm.x = ex;
nm.y = ey;
}
}
}
public var pows:powsys = adone("powsz",powsys,ui,400,240);
you are setting first parameter "powsz" but you're not using this parameter, you're just changing it with nm = new tp(); when your adone() function worked, so it is looking like working properly...
and here...
public var s1:powsyspsl = ad2("s1",powsyspsl,splz,100,100,"powsz");
you're not making the same fault as in "adone()" so it is still a string variable and you're trying to add a child to a string variable "powsz" here? i think you should fix that
par.addChild(nm);
but if you don't wanna fix things but you wanna your code work unproperly you can try this
public function ad2(nm,tp,lst,ex=0,ey=0,par=null) {
nm = new tp();
par = new tp();
par.addChild(nm);
lst.push(nm);
nm.x = ex;
nm.y = ey;
}
And all this stuff looks like a joke! Your problems not going to be solved by this way, you have to fix your whole code and logic.. You're using lots of unnecessary stuff and wrong ways to make what you want...
changed the code to:
package {
import flash.display.MovieClip;
public class main extends MovieClip {
public var ui:Array = new Array ;
public var splz:Array = new Array ;
public var pows:powsys = adone("pows",powsys,ui,400,240);
public var s1:powsyspsl = adone("s1",powsyspsl,splz,100,100,pows);
public function main() {
}
public function adone(nm,tp,lst,ex=0,ey=0,par=null) {
nm = new tp ;
if (par) {
par.addChild(nm);
} else {
addChild(nm);
}
lst.push(nm);
nm.x = ex;
nm.y = ey;
}
}
}
the "par" var returns null both times in the trace. and that means 2nd object is added to the stage not to 1st one.
Untested....
package {
import flash.display.MovieClip;
public class main extends MovieClip {
public var ui:Array = new Array;
public var splz:Array = new Array;
public var pows:powsys = adone(powsys,ui,400,240);
public var s1:powsyspsl = adone(powsyspsl,ui,400,240,pows);
public function main() {
}
public function adone(tp,lst,ex=0,ey=0,par=null):Object {
var nm = new tp();
if (par) {
par.addChild(nm);
} else {
addChild(nm);
}
lst.push(nm);
nm.x = ex;
nm.y = ey;
return nm;
}
}
}

how to convert string to object name. as3

public class ColorLibrary {
private var _allColorCodes:Object;
public function ColorLibrary() {
init();
}
private function init(){
_allColorCodes = {
'black' : '000000',
'white' : 'FFFFFF',
'yellow' : '000000'
}
}
public function exchangeColor(colors:String){
var colorArray:Array = colors.split(',');
for ( var i:int = 0; i < colorArray.length; i++ ) {
_allColorCodes.getDefinitionByName(colorArray[i]);
}
}
}
any idea how to convert string to instance name? Thanks very much~! Strugglying here
You've already got an object there, so you can already go:
_allColorCodes.black .. etc
Considering that _allColorCodes is private, you can do a really nice little getter like so:
public function get colorCode():Object
{
return _allColorCodes;
}
And then just have:
trace(colorCode.black);
trace(colorCode.yellow);
All that said, what I would do is store all this stuff against constants in a class like ColorCodes, like this:
package
{
public class ColorCodes
{
public static const BLACK:uint = 0x000000;
public static const RED:uint = 0xFF0000;
public static const BLUE:uint = 0x0000FF;
public static const GREEN:uint = 0x00FF00;
public static const WHITE:uint = 0xFFFFFF;
}
}
And then access them via:
trace(
ColorCodes.RED
);
Something slightly more advanced that you can do is make use of Proxy and flash_proxy to override the getProperty() method. This means you'll be able to go ColorLibrary.color straight off the bat:
package
{
import flash.utils.Proxy;
import flash.utils.flash_proxy;
public class ColorLibrary
{
private var _allColorCodes:Object;
public function ColorLibrary()
{
_allColorCodes = {
'black' : '000000',
'white' : 'FFFFFF',
'yellow' : '000000'
}
}
override flash_proxy function getProperty(name:*)*
{
return _allColorCodes[name];
}
}
}
Response to comment:
Okay, you don't need to use getDefinitionByName here, you can simply use brackets to access a value of an object by parsing a string.
Example:
var object:Object = {
something: 2,
anotherThing: "something awesome"
};
trace(object["something"]); // same as object.something
trace(object["anotherThing"]); // same as object.anotherThing
Try something like this for your exchangeColor() function:
public function exchangeColor(colors:String):void
{
var colorArray:Array = colors.split(',');
for each(var i:String in colorArray)
{
trace(
_allColorCodes[i]
);
}
}
I canĀ“t see why you would want to do it like this, but here is a function that passes a list of colors (String) and returns hex codes (Array). If one color does not exist you will get lost in the Array. I Recommend using ColorList.RED or at least ColorManager.getColor("red").
var colorList : Array = getColorList("black,yellow");
public function getColorList(colors : String) : Array
{
var result : Array = new Array();
var colorArray : Array = colors.split(',');
for(var i : int = 0; i < colorArray.length ; i++)
{
if(_allColorCodes[colorArray[i]])
result.push(_allColorCodes[colorArray[i]]);
}
return result;
}

In ActionScript 3, tracing all properties of a value object

I have a number of value objects and I want to be able to create a function within it to trace out the properties and the values without specifying them directly. It will allow me to use the same code in all value objects as opposed to referring to variables within the value object. Ideally, I would like to replace the code in blogURLVars with this code.
Here's a sample value object
package items {
public class Blog {
private var _itemID:uint;
private var _blogTitle:String;
private var _blogText:String;
private var _blogCreated:String;
private var _blogCategory:String;
private var _blogFrontpage:Boolean;
public function Blog (itemID:uint,blogTitle:String,blogText:String,blogCategory:String,blogCreated:String,blogFrontpage:Boolean=false) {
_itemID = itemID;
_blogTitle = blogTitle;
_blogText = blogText;
_blogCreated = blogCreated;
_blogCategory = blogCategory;
_blogFrontpage = blogFrontpage;
}
public function get itemID():uint {
return _itemID;
}
public function get blogTitle():String {
return _blogTitle;
}
public function get blogText():String {
return _blogText;
}
public function get blogCategory():String {
return _blogCategory;
}
public function get blogCreated():String {
return _blogCreated;
}
public function get blogFrontpage():Boolean {
return _blogFrontpage;
}
public function toString():void {
}
public function blogURLVars():String {
var s:String;
s = "itemID="+ _itemID;
s += "blogTitle="+ _blogTitle;
s += "blogText="+ _blogText;
s += "blogCategory="+ _blogCategory;
s += "blogCreated="+ _blogCreated;
s += "blogFrontpage="+ _blogFrontpage;
return s;
}
}
}
DescrybeType could be of help here. I'm basing this answer in this other answer (you might want to check it out): Fastest way to get an Objects values in as3
Basically, the describeType function will let you inspect the public interface of your object. That means public variables, getter/setters and methods (plus some other info not relevant to your problem). So you get a list of all the getters and with that, return the names of said properties plus their actual values for a given object. Not that this is not exactly the same as your sample code, since this will not use the private variables, but rather will call the getters.
In code, this could be something like this (based on code in the linked question).
package {
import flash.net.URLVariables;
import flash.utils.describeType;
import flash.utils.getQualifiedClassName;
public class PropertiesHelper {
public function PropertiesHelper() {
}
private static var typePropertiesCache:Object = {};
public static function getPropertyNames(instance:Object):Array {
var className:String = getQualifiedClassName(instance);
if(typePropertiesCache[className]) {
return typePropertiesCache[className];
}
var typeDef:XML = describeType(instance);
trace(typeDef);
var props:Array = [];
for each(var prop:XML in typeDef.accessor.(#access == "readwrite" || #access == "readonly")) {
props.push(prop.#name);
}
return typePropertiesCache[className] = props;
}
public static function getNameValuePairs(instance:Object):URLVariables {
var props:Array = getPropertyNames(instance);
var vars:URLVariables = new URLVariables();
for each(var prop:String in props) {
vars[prop] = instance[prop];
}
return vars;
}
}
}
Use:
var blog:Blog = new Blog(1,"title&","text","cat","created");
var urlVars:URLVariables = PropertiesHelper.getNameValuePairs(blog);
trace(urlVars);
I'm using a URLVariables object since it seems that's what you want (though not actually what you blogURLVars method does), but you could change that in the getNameValuePairs method to suit your needs if necessary. An advantage of using a URLVariables object is that it handles the url-encoding for you automatically, so reserved characters such as &, =, etc, should not be a problem.