Assume I have the following class:
class Example
{
function set something(value:String):void
{
trace("set something");
}
function doSomething():void
{
trace("something");
}
}
I can access the functions as objects like this:
var example:Example = new Example();
var asdf:Function = example.doSomething;
// this also works - example["doSomething"];
asdf(); // this trace: "something"
You do this all the time with events, for example. So, my big question is: Is there any way to get a handle on the setter? Is there some crazy function on Object or somewhere that I don't know about (please say yes :)
I want something like the following
var example:Example = new Example();
// the following won't work, because example.something is a string
var asdf:Function = example.something;
asdf("a value"); // this trace: "something"
The statement var asdf:Function = example.something; won't work because compiler treats example.something as a getter and returns string (or throws a write-only error if the getter is not implemented).
But since something is a property, you can do something like:
example["something"] = "a value"; //will trace 'set something'
//or
var property:String = "something";
example[property] = "some value"; //will trace 'set something'
You may try this:
class Example{
function set something(value:String):void{
trace("set something");
}
function doSomething():void{
trace("something");
}
}
class AnotherClass{
function callOtherClassFunction(funcObj:Obj):void{
if (funcObj.type == "method") {
funcObj.func.apply();
}
else if (funcObj.type == "setter") {
funcObj.obj[funcObj.func] = "something";
}
else if (funcObj.type == "getter") {
trace(funcObj.obj[funcObj.func]);
}
}
}
class Test{
function Test():void{
var e:Example = new Example();
var a:AnotherClass = new AnotherClass();
a.callOtherClassFunction({obj:e, type:"setter", func:"something"});
a.callOtherClassFunction({obj:e, type:"method", func:e.doSomething});
}
}
Related
I have a datagridcolumn where a labelFunction is defined:
private function myLabelFunction(item:Object, column:DataGridColumn):String
{
var returnVal:String;
var nm:NumericFormatter;
nm.decimalSeparatorTo = ".";
nm.precision = additionalParameter;
returnVal = nmTwoDecimals.format(item[column.dataField]);
if (returnVal == '0.00')
{
returnVal = '';
}
return returnVal;
}
Would it be possible to add an additional parameter so that I could pass the property values for the formatter which I intend to use?
Like for example:
private function myLabelFunction(item:Object, column:DataGridColumn, precisionParam:int):String
{
var returnVal:String;
var nm:NumericFormatter;
nm.decimalSeparatorTo = ".";
nm.precision = precisionParam;
returnVal = nmTwoDecimals.format(item[column.dataField]);
if (returnVal == '0.00')
{
returnVal = '';
}
return returnVal;
}
Thanks.
You would have to extend the DataGridColumn class. After creating your new class simply override the existing itemToLabel function:
public function itemToLabel(data:Object):String
{
if (!data)
return " ";
if (labelFunction != null)
return labelFunction(data, this);
if (owner.labelFunction != null)
return owner.labelFunction(data, this);
if (typeof(data) == "object" || typeof(data) == "xml")
{
try
{
if ( !hasComplexFieldName )
data = data[dataField];
else
data = deriveComplexColumnData( data );
}
catch(e:Error)
{
data = null;
}
}
if (data is String)
return String(data);
try
{
return data.toString();
}
catch(e:Error)
{
}
return " ";
}
The line 'return labelFunction(data, this);' is what calls the labelFunction (will also check the owner datagrid for a labelfunction). 'data' in 'itemToLabel' is your object. You could either include the precision value you want in the object or hard code it in the extended class (or inject, or singleton, class var, whatever you like).
At this point you can go ahead and pass it as a third parameter to your new labelFunction.
In your label function for the datagrid column, you can access the assigned data field by using the dataField property, see the following syntax below:
"supposing your label function is called formatNumbers_LabelFunction"
private function formatNumbers_LabelFunction(item:Object, column:DataGridColumn):String{
//Write any code logic you want to apply on your data field ;)
//In this example, I'm using a number formatter to edit numbers
return myCustomNumberFormatter.format(item[column.dataField]);
}
This way, you can use a generic label function to handle some unified operations on your displayed data
And besides that, you can also access to any data field that is in the data provider by just calling its name like this:
item.YourFieldName
where item is the firs parameter [of type Object] in your label function method
This would work:
<DataGridColumn labelFunction="{function(item:Object, column:DataGridColumn):String { return anotherLabelFunction(item,column,2) }}" />
// Elsewhere ...
function anotherLabelFunction(item:Object,column:DataGridColumn,precision:int):String
{
// Do your business
}
My goal is to create a generic function that selects a value in a combobox accoring to a value.
(My comoBox holds arrayCollection as dataProvider.)
The difficulty is infact to get a propertyname in runtime mode
public function selectComboByLabel(combo:ComboBox , propetryName:String, value:String):void {
var dp:ArrayCollection = combo.dataProvider as ArrayCollection;
for (var i:int=0;i<dp.length;i++) {
if (dp.getItemAt(i).propertyName==value) {
combo.selectedIndex = i;
return;
}
}
}
the line if (dp.getItemAt(i).propertyName==value)
is of course incorrect.
It should be arther something like: dp.getItemAt(i).getPropertyByName(propertyName)
Any clue on how to that ?
Don't use Object Property notation. Do this:
dp.getItemAt(i)[propertyName]
In addition to what Flextras said, you could also redo your for loop to make it easier to read:
for each(var item:Object in dp) {
if(item[propertyName] == value) {
combo.selectedItem = item;
return;
}
}
I have many Flex objects like this one:
public class MyData {
public var time: Date;
public var label: String;
}
I am populating this object from a DB record retrieved via AMF that looks something like this:
{
label: "Label",
incident: "2009-08-15 11:12:14.12233"
}
I want to write a generic value mapper for these object that, given a target object (instance of MyData here) and an input record, will be able to tell that MyData.time is a Date field and perform type mapping automatically. Something like this:
function map(obj, targetType): * {
var newInstance: * = new targetType();
for (var property: String in obj) {
if (getPropertyType(targetType, property) == Date) {
newInstance[property] = parseDate(obj[property]);
}
else {
newInstance[property] = obj[property];
}
}
}
function getPropertyType(type_var: Class, property: String): Class {
// .. this is what I have no idea how to do
}
Can someone fill in the blank here?
You possibly need something like describeType. And maybe you need to use getDefinitionByName() if you want to make to a real object. So something like this for the contents of your function:
var typeXml:XML = describeType(type_var[property]);
return getDefinitionByName(typeXml.type[0].#name);
I haven't compiled it. Just throwing it out there to see if it helps.
You can use the 'is' operator to check the type of an object.
The is operator
function map(obj, targetType): * {
var newInstance: * = new targetType();
for (var property: String in obj) {
if (obj[property] is Date) {
newInstance[property] = parseDate(obj[property]);
}
else {
newInstance[property] = obj[property];
}
}
}
hth
Koen
If you need to map an Object variable to a variable class as MyData you can do the following
public class MyData
{
public var time: Date;
public var label: String;
function map(obj:Object):void
{
for (var property: String in obj)
{
this[property] = obj[property];
}
}
}
Note: The object obj must contain the exact "time" and "label" properties.
Hope it solves your problem
OK, I am sorting an XMLListCollection in alphabetical order. I have one issue though. If the value is "ALL" I want it to be first in the list. In most cases this happens already but values that are numbers are being sorted before "ALL". I want "ALL" to always be the first selection in my dataProvider and then the rest alphabetical.
So I am trying to write my own sort function. Is there a way I can check if one of the values is all, and if not tell it to do the regular compare on the values?
Here is what I have:
function myCompare(a:Object, b:Object, fields:Array = null):int
{
if(String(a).toLowerCase() == 'all')
{
return -1;
}
else
if(String(b).toLowerCase() == 'all')
{
return 1;
}
// NEED to return default comparison results here?
}
//------------------------------
var sort:Sort = new Sort();
sort.compareFunction = myCompare;
Is there a solution for what I am trying to do?
The solution from John Isaacks is awesome, but he forgot about "fields" variable and his example doesn't work for more complicated objects (other than Strings)
Example:
// collection with custom objects. We want to sort them on "value" property
// var item:CustomObject = new CustomObject();
// item.name = 'Test';
// item.value = 'Simple Value';
var collection:ArrayCollection = new ArrayCollection();
var s:Sort = new Sort();
s.fields = [new SortField("value")];
s.compareFunction = myCompare;
collection.sort = s;
collection.refresh();
private function myCompare(a:Object, b:Object, fields:Array = null):int
{
if(String((a as CustomObject).value).toLowerCase() == 'all')
{
return -1;
}
else if(String((b as CustomObject).value).toLowerCase() == 'all')
{
return 1;
}
// NEED to return default comparison results here?
var s:Sort = new Sort();
s.fields = fields;
var f:Function = s.compareFunction;
return f.call(null,a,b,fields);
}
Well I tried something out, and I am really surprised it actually worked, but here is what I did.
The Sort class has a private function called internalCompare. Since it is private you cannot call it. BUT there is a getter function called compareFunction, and if no compare function is defined it returns a reference to the internalCompare function. So what I did was get this reference and then call it.
private function myCompare(a:Object, b:Object, fields:Array = null):int
{
if(String(a).toLowerCase() == 'all')
{
return -1;
}
else if(String(b).toLowerCase() == 'all')
{
return 1;
}
// NEED to return default comparison results here?
var s:Sort = new Sort();
var f:Function = s.compareFunction;
return f.call(null,a,b,fields);
}
Thanks guys, this helped a lot. In our case, we needed all empty rows (in a DataGrid) on the bottom. All non-empty rows should be sorted normally. Our row data is all dynamic Objects (converted from JSON) -- the call to ValidationHelper.hasData() simply checks if the row is empty. For some reason the fields sometimes contain the dataField String value instead of SortFields, hence the check before setting the 'fields' property:
private function compareEmptyAlwaysLast(a:Object, b:Object, fields:Array = null):int {
var result:int;
if (!ValidationHelper.hasData(a)) {
result = 1;
} else if (!ValidationHelper.hasData(b)) {
result = -1;
} else {
if (fields && fields.length > 0 && fields[0] is SortField) {
STATIC_SORT.fields = fields;
}
var f:Function = STATIC_SORT.compareFunction;
result = f.call(null,a,b,fields);
}
return result;
}
I didn't find these approaches to work for my situation, which was to alphabetize a list of Strings and then append a 'Create new...' item at the end of the list.
The way I handled things is a little inelegant, but reliable.
I sorted my ArrayCollection of Strings, called orgNameList, with an alpha sort, like so:
var alphaSort:Sort = new Sort();
alphaSort.fields = [new SortField(null, true)];
orgNameList.sort = alphaSort;
orgNameList.refresh();
Then I copied the elements of the sorted list into a new ArrayCollection, called customerDataList. The result being that the new ArrayCollection of elements are in alphabetical order, but are not under the influence of a Sort object. So, adding a new element will add it to the end of the ArrayCollection. Likewise, adding an item to a particular index in the ArrayCollection will also work as expected.
for each(var org:String in orgNameList)
{
customerDataList.addItem(org);
}
Then I just tacked on the 'Create new...' item, like this:
if(userIsAllowedToCreateNewCustomer)
{
customerDataList.addItem(CREATE_NEW);
customerDataList.refresh();
}
I want to check in my function if a passed argument of type object is empty or not. Sometimes it is empty but still not null thus I can not rely on null condition. Is there some property like 'length'/'size' for flex objects which I can use here.
Please help.
Thanks in advance.
If you mean if an Object has no properties:
var isEmpty:Boolean = true;
for (var n in obj) { isEmpty = false; break; }
This is some serious hack but you can use:
Object.prototype.isEmpty = function():Boolean {
for(var i in this)
if(i != "isEmpty")
return false
return true
}
var p = {};
trace(p.isEmpty()); // true
var p2 = {a:1}
trace(p2.isEmpty()); // false
You can also try:
ObjectUtil.getClassInfo(obj).properties.length > 0
The good thing about it is that getClassInfo gives you much more info about the object, eg. you get the names of all the properties in the object, which might come in handy.
If object containes some 'text' but as3 doesn't recognize it as a String, convert it to string and check if it's empty.
var checkObject:String = myObject;
if(checkObject == '')
{
trace('object is empty');
}
Depends on what your object is, or rather what you expect it to have. For example if your object is supposed to contain some property called name that you are looking for, you might do
if(objSomeItem == null || objSomeItem.name == null || objSomeItem.name.length == 0)
{
trace("object is empty");
}
or if your object is actually supposed to be something else, like an array you could do
var arySomeItems = objSomeItem as Array;
if(objSomeItem == null || arySomeItems == null || arySomeItems.length == 0)
{
trace("object is empty");
}
You could also use other ways through reflection, such as ObjectUtil.getClassInfo, then enumerate through the properties to check for set values.... this class help:
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
public class ReflectionUtils
{
/** Returns an Array of All Properties of the supplied object */
public static function GetVariableNames(objItem:Object):Array
{
var xmlPropsList:XMLList = describeType(objItem)..variable;
var aryVariables:Array = new Array();
if (xmlPropsList != null)
{
for (var i:int; i < xmlPropsList.length(); i++)
{
aryVariables.push(xmlPropsList[i].#name);
}
}
return aryVariables;
}
/** Returns the Strongly Typed class of the specified library item */
public static function GetClassByName($sLinkageName:String):Class
{
var tObject:Class = getDefinitionByName($sLinkageName) as Class;
return tObject;
}
/** Constructs an instance of the speicified library item */
public static function ConstructClassByName($sLinkageName:String):Object
{
var tObject:Class = GetClassByName($sLinkageName);
//trace("Found Class: " + tMCDefinition);
var objItem:* = new tObject();
return objItem;
}
public static function DumpObject(sItemName:String, objItem:Object):void
{
trace("*********** Object Dump: " + sItemName + " ***************");
for (var sKey:String in objItem)
{
trace(" " + sKey +": " + objItem[sKey]);
}
}
//}
}
Another thing to note is you can use a simple for loop to check through an objects properties, thats what this dumpobject function is doing.
You can directly check it as follow,
var obj:Object = new Object();
if(obj == null)
{
//Do something
}
I stole this from a similar question relating to JS. It requires FP 11+ or a JSON.as library.
function isEmptyObject(obj){
return JSON.stringify(obj) === '{}';
}
can use use the hasProperty method to check for length
var i:int = myObject.hasProperty("length") ? myObject.length: 0;