Let's say I have a class which looks like this :
public class MyClass
{
public var attribute1;
public var attribute2;
}
and I'd like to get attribute1 and attribute2 as strings. I tried this :
var test:MyClass = new MyClass();
for (var key:String in test)
{
trace(test[key]);
}
but it does not work, it never goes in the loop. How can I do what I want to do ?
Thanks
A for..in loop enumerates only dynamically added properties. Declared variables and methods of classes are not enumerated in for..in loops. This means that most classes in the ActionScript API will not display any properties in a for..in loop.
For a solution read:
http://livedocs.adobe.com/flex/3/html/help.html?content=usingas_8.html
You'll need to use AS reflection/introspection.
The native way is using describeType function, like this:
public function getDetails():void {
var classInfo:XML = describeType(button1);
// List the class name.
ta1.text = "Class " + classInfo.#name.toString() + "\n";
// List the object's variables, their values, and their types.
for each (var v:XML in classInfo..variable) {
ta1.text += "Variable " + v.#name + "=" + button1[v.#name] +
" (" + v.#type + ")\n";
}
}
(from flex doc: http://livedocs.adobe.com/flex/3/html/help.html?content=usingas_8.html)
I'm not sure but I think the class mx.utils.ObjectUtil can make it simpler. And it's still native way.
Another option is using a library to make it easier.
Look this one: http://www.as3commons.org/as3-commons-reflect/introduction.html
Related
I have 2 dynamic objects and I want to build one to contain all the properties:
var o1:Object = {prop1:val1,prop2:val2,prop3:val3};
var o2:Object = {prop3:val3a,prop4:val4};
and I need to obtain a third object that looks like that:
{prop1:val1, prop2:val2, prop3:val3a, prop4:val4};
Basically I need a way to iterate through the object properties and to add new properties to the third object. I have to mention I'm quite new to AS3/Flash/Flex.
First question, do you really mean to have prop3 in both objects? you will need to decide what to do in case of a collision like that, which object has precedence.
Secondly, check out the introspection apis: http://livedocs.adobe.com/flex/3/html/help.html?content=usingas_8.html
something like this should work:
public function mergeDynamicObjects ( objectA:Object, objectB:Object ) : Object
{
var objectC:Object = new Object();
var p:String;
for (p in objectA) {
objectC[p] = objectA[p];
}
for (p in objectB) {
objectC[p] = objectB[p];
}
return objectC;
}
If the property exists in A and B, B's will overwrite A's. Also note that if the values of a property is an object, it will pass a reference, not a copy of the value. You might need to clone the object in those cases, depending on your needs.
Note: I haven't actually tested the above, but it should be close. Let me know if it doesn't work.
Updated to fix the errors. Glad it works for you though.
You can dynamically access/set properties on objects with the index operator. The for loop will itterate over the property names, so if you put it all together, the following test passes:
[Test]
public function merge_objects():void {
var o1:Object = {prop1:"one", prop2:"two", prop3:"three"};
var o2:Object = {prop3:"threeA", prop4:"four"};
var o3:Object = new Object();
for (var prop in o1) o3[prop] = o1[prop];
for (var prop in o2) o3[prop] = o2[prop];
assertThat(o3.prop1, equalTo("one"));
assertThat(o3.prop2, equalTo("two"));
assertThat(o3.prop3, equalTo("threeA"));
assertThat(o3.prop4, equalTo("four"));
}
you can iterate over the object properties like:
var obj1:Object = new Object();
for(var str:String in obj2){
obj1[str] = "any value"; // insert the property from obj2 to obj1
}
When Flex Sees Something Like This:
<mx:Label text="Hello {MyVar} World!"/>
It Must Translate That Somehow Into ActionScript. But What If I Need To Do Something Similar, At Runtime. How Can I Accomplish What DYNAMICALLY? WHEN I DO NOT KNOW THE CONTENTS OF THE BINDING TEMPLATE.
In ActionScript it would need it to look something like this:
public function CustomDynamicBinding(StringToBind:String):Label {
// *EXAMPLES* Of StringToBind:
// "Hello {MyVar} World!"
// "Product: {name} ${price}.00"
// "{data.label}, {data.description}"
// I've Written It This Way Because I DO NOT KNOW The Exact Text To Be Bound At Design Time.
[Bindable]
var Lab:Label=new Label();
Lab.text=???
return(Lab);
}
How can I accomplish this kind of "Dynamic" binding... Where I don't know the value of "StringToBind" until runtime? For the purposes of this question we can assume that I do know that any variable(s) mentioned in "StringToBind", are guaranteed to exist at runtime.
I already realize there are much more straightforward ways to accomplish this exact thing STATICALLY, and using only Flex/MXML. It's important for my project though that I understand how this could be accomplished without MXML.
Doing This:
lab.text = stringToBind.replace("{myVar}", str);
Will NOT work because this simply assigns ONCE the value of "{myVar}" - (which may not even BE the variable referenced in "stringToBind"!!) to the label, and does not take into account when and if myVar changes! Wouldn't I need to somehow use something Like bindProperty?
Use BindingUtils.bindSetter
var stringToBind:String = "Hello {myVar} World!";
[Bindable]
var myVar:String = 'Flex';
var lab:Label = new Label();
BindingUtils.bindSetter(labelValue, this, "myVar");
function set labelValue(str:String):void
{
lab.text = "Hello " + str + " World!";
//or if you want it dynamic
lab.text = stringToBind.replace("{myVar}", str);
}
Note that this is not pure ActionScript in its strict sense as data binding itself is a Flex concept; this is just MXML-less syntax of doing it. You're still using Flex binding internally - but again, use of Label alone makes if Flexy
private function _BindingSource_bindingsSetup():Array
{
var result:Array = [];
result[0] = new mx.binding.Binding(this,
function():String
{
var result:* = "Hello " + (MyVar) + " World!";
return (result == undefined ? null : String(result));
},
null,
"_BindingSource_Label1.text"
);
return result;
}
It's only a part of generated code. Feel free to add -keep-generated-actionscript parameter to compiler options and read all generated ActionScript in bin-debug\generated.
Disclosure: shameless self promotion
The BindageTools library provides an intuitive builder API for setting up bindings in ActionScript.
OK I am looping through the properties in an object like so:
private var _propsList:Object = {'Type':'product_type'
,'Kind':'product_type_sub'
,'Stone':'primary_stone'
,'Stone Color':'primary_stone_sub'
,'Metal':'metal_type'
,'Brand':'product_brand'};
for(key in _propsList)
{
val = _propsList[key];
trace(key +" = "+ val);
}
I am expecting the first trace to be Type = property_type since that is the first one defined in the array, however it is coming up random everytime. I guess this is because my keys are strings and not integers, however is there a way to specify the order it loops through them?
Thanks!!
You can't rely on for (v in someObject) ... to return things in a predictable order, no.
Depending on your specific situation, you could just use an array to hold the keys, and just iterate through that:
private var keys:Array = ["Type", "Kind", "Stone", "Stone Color", "Metal", "Brand"];
private function iterate():void
{
for each (var k:String in keys)
{
trace(_propsList[k]);
}
}
Maybe a bit obvious or non-elegant, but it'd get the job done. :)
you could hack it by classing-out your "_propsList" object creating an array inside of the newly created PropsList class that references the properties in order. At that point, you could run a FOR loop on the array and get your properties in order.
OR, you could have a function inside that new class that would return an Array of those properties. like this:
public function getProps():Array {
return [myPropertyOne, myPropertyTwo, myPropertyThree];
}
In general, I think this is a case where you shouldn't depend on a particular behavior from the framework/language you are using. This type of behavior is generally poorly documented and can change from version to version.
If you really need a specific retrieval order, I would create a wrapper class as jevinkones suggested above. Maybe there's even a utility class in the framework somewhere to accomplish this (Dictionary, etc.?)
HTH,
Karthik
I found this link that gives some background:
Subtle Change in for..in Loops for ActionScript 3
This question is actually a dup of this one.
How about using an array representation like this:
var _propsList:Array = [
['Type', 'product_type'],
['Kind', 'product_type_sub'],
['Stone', 'primary_stone'],
['Stone Color', 'primary_stone_sub'],
['Metal', 'metal_type'],
['Brand', 'product_brand']
];
for(var i in _propsList) {
var elem = _propsList[i];
var key = elem[0];
var val = elem[1]
}
I am trying to get the contents of an arraycollection to print out using my debug function (which takes a string). Anyone know how to do this? I would like it would be rather easy but can't seem to find a way...I get the word "Object" printed a lot of the time.
It's a lot cleaner to do:
var str:String = '['+myArrayCol.source.join(', ')+']';
the source property of an ArrayCollection is an Array, so all the usual functions are available.
The following method should get you what you need:
public static function arrayCollectionToString( arr:ArrayCollection ):String
{
var toRet:String = "[";
for each( var obj:Object in arr ) {
toRet += obj.toString() + ", ";
}
toRet += "]";
return toRet;
}
If you stick this in the same class as your debug method, you could then use it as follows:
SomeDebugClass.dbgMessage( SomeDebugClass.arrayCollectionToString( myArrayCollection ) );
Default is allready coma separated
array.toString()
You can use
ObjectUtil.toString(arrayCollection);
I've created an ASP.Net user control that will get placed more than once inside of web page. In this control I've defined a javascript object such as:
function MyObject( options )
{
this.x = options.x;
}
MyObject.prototype.someFunction=function someFunctionF()
{
return this.x + 1;
}
In the code behind I've created MyObject in a startup script --
var opts = { x: 99 };
var myObject = new MyObject( opts );
When a certain button in the control is pressed it will call myObject.someFunction(). Now lets say the value of x will be 99 for one control but 98 for another control. The problem here is that the var myObject will be repeated and only the last instance will matter. Surely there's a way to make the var myObject unique using some concept I've haven't run across yet. Ideas?
Thanks,
Craig
Your Javascript like this:-
function MyObject(options) { this.x = options.x; }
MyObject.prototype.someFunction = function() { return this.x + 1; }
MyObject.create(id, options) {
if (!this._instances) this._instances = {};
return this._instances[id] = new MyObject(options);
}
MyObject.getInstance(id) { return this._instances[id]; }
Your startup javascript like this:-
MyObject.create(ClientID, {x: 99});
Other code that needs to use an instance (say in the client-side onclick event)
String.Format("onclick=\"MyObject.getInstance('{0}').someFunction()\", ClientID);
Note the low impact on the clients global namespace, only the MyObject identifier is added to the global namespace, regardless of how many instances of your control are added to the page.
If it is just one value, why not have the function take it as a parameter and build your onclick handler so that it puts the correct value in for each control. If it is more complex than that, then consider making options an array and, for each control, insert the correct options into the spot in the array that corresponds to each particular control. Then pass the proper index into the array into the function.
I do this by using ScriptManager.RegisterClientScriptBlock to register a string as a JavaScript block on the client side. I can then modify my script string using {0}, {1}..,{n} place holders to inject necessary ids. It depends on the structure of your code as to if this is the most elegant fashion, but it works in a pinch. You could then inject variable names using references to Me.ClientID.
You can make the value of "x" static and access it anywhere in the code, such as:
function MyObject( options ) { MyObject.x = options.x; }
MyObject.x = 99; // static
MyObject.prototype.someFunction = function () { return MyObject.x + 1; }
This way you can access MyObject.x anywhere in your code, even without re-instanciating MyObject.
Excellent solution Anthony. The other solutions offered were as good and I did consider them but I was looking for something a little more elegant like this solution.
Thanks!