AS3: Is it possible to create a variable to hold instance name? - actionscript-3

I am trying to have a more dynamic function and would like to allow the functions instance name were it outputs the text to be changeable.
for example
function example_function(url,instance_name){
instance_name.text = url;
}
example_function('www.example.com','url_txt');
example_function('www.another.com','more_txt');
Is this possible?

Yes, just parse the string into square brackets next to the instance's owner. For example:
this[instance_name].text = url;
More info:
Take this object:
var obj:Object = {
property1: 10,
property2: "hello"
};
Its properties can be accessed either as you'd expect:
obj.property1;
obj.property2;
Or as mentioned above:
obj["property1"];
obj["property2"];
I suggest using a function like this one I've created to tighten your code up a bit:
function selectProperty(owner:*, property:String):*
{
if(owner.hasOwnProperty(property)) return owner[property];
else throw new Error(owner + " does not have a property \"" + property + "\".");
return null;
}
trace(selectProperty(stage, "x")); // 0
trace(selectProperty(stage, "test")); // error

It is definitely possible, but it's not really best practice to do it with Strings like that. Instead, you can pass in a reference to the variable you're trying to modify.
function example_function(url : String, instance : TextField) : void {
instance.text = url;
}
example_function("www.example.com", url_txt);
This gives you strong typing so you can tell at compile time if you're operating on a TextField or not. If you aren't, you'll get an error because the 'text' property doesn't exist. You'll be able to find and track down errors faster this way.
However, if you must do it with Strings, you can access any property on any object using a string key like:
var myInstance = this[instance_name]
So in your example, you could do:
function example_function(url : String, instance : TextField) : void {
this[instance_name].text = url;
}
example_function("www.example.com", "url_txt");

Related

Typsescript object from json casting to type

In daily life I need to read some json from ajax and cast it to some Typed object (including its METHODS). On internet I found and use following code to type casting:
export class Obj
{
public static cast<T>(obj, type: { new(...args): T} ): T
{
obj.__proto__ = type.prototype;
return obj;
}
}
As example It can be use in following way:
let objFromJson = { id: 666, name: "love" };
let building: Building = null;
building = Obj.cast(objFromJson, Building);
// On this point constructor for Building is not call - this is
// correct because we not create object but only make type casting
building.test('xx');
// on this point on console we should get:
// > "building.test a:xx, name:love"
// so object 'building' indeed have methods of Building Class
where (example is from 'head')
export class Building {
constructor(
public id: number,
public name: string,
) {
console.log('building.constructor: id:' + id + ', name' + name);
}
public test(a) {
console.log('building.test a:' + a + ', name:' + this.name);
}
}
Additional info: Instead of using cast<T>(obj, type: { new(...args): T} ): T we can use just cast<T>(obj, type): T but I read that second version will cause problem with arrow functions (https://stackoverflow.com/a/32186367/860099) - I don't understand why - ?
Questions: I not really understand how method Obj.cast works (and for instance how I can use ...args on calling it) - can someone explain it? Do someone know alternative function but not for casting but for CREATE object (so call constructor) form json data in similar handy way (eg. building = Obj.create(objFromJson, Building);
How cast works
Javascript uses prototypical inheritance. The __proto__ property, represents the prototype of the current object. This is what determines the type of the given object
For objects created using an object literal, this value is Object.prototype. For objects created using array literals, this value is Array.prototype. For functions, this value is Function.prototype. For objects created using new fun, where fun is one of the built-in constructor functions provided by JavaScript (Array, Boolean, Date, Number, Object, String, and so on — including new constructors added as JavaScript evolves), this value is always fun.prototype. For objects created using new fun, where fun is a function defined in a script, this value is the value of fun.prototype.
So when you change the __proto__ you change the prototype chain for the object basically changing it's type.
type: { new(...args): T} it the way you can represent a constructor function in typescript. As the above quote says for an object constructed by a function (such as type) the __ptoto__ should be the prototype of the function.
So when setting the __proto__, cast basically simulates the fact that the object was constructed using the type constructor function passed as a parameter.
Problems with this approach
The problems is that the constructor doesn't actually get invoked, you are just simulating the fact that the object was created using the given constructor. So any initialization that occurs in the constructor does not get executed. Arrow functions for example need to capture this, so they are not set on the prototype of the constructor function but rather on the instance of the object during the invocation of the constructor so if the constructor does not invoked, arrow functions are not initialized:
export class Building {
private otherField : string;
constructor(
public id: number,
public name: string,
) {
console.log('building.constructor: id:' + id + ', name' + name);
this.otherField = name+id;
// Ts adds in the code for initializing arrow functions in JS, but the idea is the same, this is where it would happen
}
public arrow = ()=> {};
public test(a) {
console.log('building.test a:' + a + ', name:' + this.name);
// both fields below will be undefined if cast was used.
console.log('building.otherField' + this.otherField + ', arrow:' + this.arrow); }
}
Alternative to cast
An alternative would be to create a new instance of the class and use Object.assign to assign the properties from the json object. At first glance this may seem slower, but the documentation says changing the __ptoto__ is very slow and not recommended
Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation, in every browser and JavaScript engine.
export class Building {
public id: number;
public name: string;
constructor(data: Partial<Building>){
Object.assign(this, data)
console.log('building.constructor: id:' + this.id + ', name' + this.name);
}
public test(a) {
console.log('building.test a:' + a + ', name:' + this.name);
}
}
let objFromJson = { id: 666, name: "love" };
let building: Building = new Building(objFromJson);
If the class does not have any methods, and maybe you can change your design so it does not, then I would just use an interface to type the JSON object and keep using the original JSON object.

AS3 variable declared as a null function

I have encountered an AS3 function that is declared as a null variable, as in:
public var edgeWeights:Function = null;
I am not sure how to use this function to change null to another value (e.g., a number like 2 or 3). I thought something like cs.edgeWeights = 2 might work, but that creates a compile error, as does cs.edgeWeights(2);
I believe these are anonymous functions in AS3 and I did do some research on them, but could not find a resolution to this situation.
public var edgeWeights:Function = null;
This notation means declaring variable edgeWeights of type Function. In Actionscript Function is an object and can be set to null.
To use it you need to set this variable to some function. For example:
edgeWeights = function(a:int,b:int):int { return a+b } or edgeWeights = Math.sin.
What function you should set there depends on your particular case.
If you assume that the Class that declares edgeWeights is Widget:
protected var widget:Widget;
protected function createWidget():void {
widget = new Widget();
widget.edgeWeights = widgetCallback;
}
//signature would need to match what the Widget
//actually expects this callback to do
protected function widgetCallback():void {
trace('hi from widget callback');
}
Note that it's probably bad practice to have a public callback variable and not provide a default implementation, so if you have access to the source code, you should probably fix that.
Given any function:
public function someFunction()
{
...
}
You can create a "pointer" with this: this.edgeWeights = someFunction; (yes, without ())
Later you just use: this.edgeWeights(); and you'll be calling someFunction().

Underscores in MvvmCross data binding

I'm using the fluent syntax and lambdas for data binding in MvvmCross. An example of this is:
var bindings = this.CreateBindingSet<MyTableCell, MyTableCellViewModel>();
bindings.Bind(titleLabel).To(vm => vm.MY_TITLE);
bindings.Apply();
Whenever I try this with an underscore in a view model property I get an exception:
Cirrious.CrossCore.Exceptions.MvxException: Unexpected character _ at
position 3 in targetProperty text MY_TITLE
I believe the error message is a result of MvvmCross parsing the data binding, yet this seems to only make sense for people using string-based data binding, not the lambda expression syntax.
Unfortunately, I cannot change the view models so I'm looking for a workaround to allow underscores in the view models. Any ideas?
I'd guess this is a general problem in the MvvmCross parser - probably in
private void ParsePropertyName()
{
var propertyText = new StringBuilder();
while (!IsComplete && char.IsLetterOrDigit(CurrentChar))
{
propertyText.Append(CurrentChar);
MoveNext();
}
var text = propertyText.ToString();
CurrentTokens.Add(new MvxPropertyNamePropertyToken(text));
}
In https://github.com/MvvmCross/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Binding/Parse/PropertyPath/MvxSourcePropertyPathParser.cs#L80
Which probably needs to be fixed to something like:
while (!IsComplete &&
(char.IsLetterOrDigit(CurrentChar) || CurrentChar == '_')
There are workarounds you could do, but the easiest solution is probably to fix this and rebuild, rather than to try workarounds.
But if you do want to try workarounds....
Assuming this is static (non-changing) text and this is just a one-off for now, then one workaround might be to add a property to your cell called Hack and to then bind like:
bindings.Bind(this).For(v => v.Hack).To(vm => vm);
//...
private MyTableCellViewModel _hack;
public MyTableCellViewModel Hack
{
get { return _hack; }
set { _hack = value; if (_hack != null) titleLabel.Text = _hack.MY_VALUE; }
}
Another alternative (with the same assumptions) might be to use a value converter -
bindings.Bind(titleLabel).To(vm => vm.MY_TITLE).WithConversion(new WorkaroundConverter(), null);
// ...
public class WorkaroundConverter : MvxValueConverter<MyTableCellViewModel, string>
{
protected override string Convert(MyTableCellViewModel vm, /*...*/)
{
if (vm == null) return null;
return vm.MY_TITLE;
}
}

How to export data from LinqPAD as JSON?

I want to create a JSON file for use as part of a simple web prototyping exercise. LinqPAD is perfect for accessing the data from my DB in just the shape I need, however I cannot get it out as JSON very easily.
I don't really care what the schema is, because I can adapt my JavaScript to work with whatever is returned.
Is this possible?
A more fluent solution is to add the following methods to the "My Extensions" File in Linqpad:
public static String DumpJson<T>(this T obj)
{
return
obj
.ToJson()
.Dump();
}
public static String ToJson<T>(this T obj)
{
return
new System.Web.Script.Serialization.JavaScriptSerializer()
.Serialize(obj);
}
Then you can use them like this in any query you like:
Enumerable.Range(1, 10)
.Select(i =>
new
{
Index = i,
IndexTimesTen = i * 10,
})
.DumpJson();
I added "ToJson" separately so it can be used in with "Expessions".
This is not directly supported, and I have opened a feature request here. Vote for it if you would also find this useful.
A workaround for now is to do the following:
Set the language to C# Statement(s)
Add an assembly reference (press F4) to System.Web.Extensions.dll
In the same dialog, add a namespace import to System.Web.Script.Serialization
Use code like the following to dump out your query as JSON
new JavaScriptSerializer().Serialize(query).Dump();
There's a solution with Json.NET since it does indented formatting, and renders Json dates properly. Add Json.NET from NuGet, and refer to Newtonsoft.Json.dll to your “My Extensions” query and as well the following code :
public static object DumpJson(this object value, string description = null)
{
return GetJson(value).Dump(description);
}
private static object GetJson(object value)
{
object dump = value;
var strValue = value as string;
if (strValue != null)
{
var obj = JsonConvert.DeserializeObject(strValue);
dump = JsonConvert.SerializeObject(obj, Newtonsoft.Json.Formatting.Indented);
}
else
{
dump = JsonConvert.SerializeObject(value, Newtonsoft.Json.Formatting.Indented);
}
return dump;
}
Use .DumpJson() as .Dump() to render the result. It's possible to override more .DumpJson() with different signatures if necessary.
As of version 4.47, LINQPad has the ability to export JSON built in. Combined with the new lprun.exe utility, it can also satisfy your needs.
http://www.linqpad.net/lprun.aspx

AS3 - Returning a property of a class rather than the class itself

In ActionScript 3, there are some classes that will represent a value rather than the class itself. It's hard to explain properly what I mean, so take this example:
var str:String = "something";
var mc:MovieClip = new MovieClip();
trace(str); // something
trace(mc); // [object MovieClip]
You'll notice that the first trace outputs a value, rather than [object String]. Ontop of this, I can still make use of methods of String, like this:
var ar:Array = str.split('s');
Even though in a way you could almost read the above as:
"something".split('s');
I have a class AvLevelData that has some methods that deal with level data (which is essentially a String). At the moment there is a property data:String which represents the core level data.
The question I have is - can I replicate the behaviour of String in that when I trace or assign an instance of AvLevelData, the result is actually the String data.
For example, at the moment I need to go:
var levelData:AvLevelData = new AvLevelData();
trace(levelData.data);
To get the data. I instead want to be able to simply do the following:
var levelData:AvLevelData = new AvLevelData();
trace(levelData); // some level data string
Is this possible?
If you wan't your object to trace out your own fabricated string then you must implement a toString() function on your AvLevelData class.
In your example above, the MovieClip trace outputs: [Object MovieClip]; this comes from the default toString() implementation for Object (found on Object.prototype) . Note, you cannot override toString() as it only exists on the prototype of Object (remnants of the AS2/Javascript world), all you need to do is provide your own implementation with the same name. For instance:
public function toString():String {
return "MyCustomObjectString";
}
Some of the most basic types - String, int, Number, uint, Boolean, to name a few - are not classes / objects per se, they are primitives. In some languages there is a wrapper class available for some of these so they can be treated like objects, though Flash doesn't do this so much from my experience.
Probably the best way to answer your question is to make a toString() method for your AvLevelData class:
public function toString():String {
return data;
}
Any time you treat a class as a string (such as by putting it in trace()), flash (and many other languages) try to call toString() on the object. Typically this results in a string that's not helpful. But if you define your own toString() method, you can control what string gets output.
Another option is to simply do:
trace(AvLevelData.data);
Since that variable is a string, it should trace just fine.