[update]
To call a function when a property of an object changes, use the following code
BindingUtils.bindSetter(callThisFunction, aObject, "prop");
Note, callThisFunction is a setter (need one argument and return void)
In MXML it’s easy to bind data using {}. Sometimes you may prefer to use actionscript as it is more efficient. How to achieve data binding in actionscript? The ultimate question is, how do you know when data changes? Here I only focus on complex data (i.e. custom Object. e.g. a Person Object which has two variable, name and age) instead of simple ones (String, int, etc). For complex data, there are two types of changes:
- Reference change
This is caused bynew
operator. (e.g.p = new Person('Mike',32);
) - Property change
This is caused by directly changing the properties (e.g.p.name = 'John'
orp.age = 20;
). The reference of the object is not changed.
We apparently want to detect both changes. First, we need to use [Bindable] metatag for the Person class. Otherwise, we won’t be able to detect property change.
package { [Bindable] // necessary if you want to detect changes when individual properties are changed but the reference not changed public class Person { public var name:String = ''; public var age:int = 0; public function Person(name:String=null, age:int=undefined):void { this.name = name; this.age = age; } } }
Then we create our custom component using actionscript only. This custom component, we call DisplayPersonComponent, is to display name and age in TextInput. It also has a member variable person:Person.
package { import mx.binding.utils.BindingUtils; import mx.binding.utils.ChangeWatcher; import mx.containers.VBox; import mx.controls.TextInput; public class DisplayPersonComponent extends VBox { [Bindable] public var person:Person; public var nti:TextInput; // to display name public var ati:TextInput; // to display age public function DisplayPersonComponent() { super(); nti = new TextInput(); ati = new TextInput(); this.addChild(nti); this.addChild(ati); ChangeWatcher.watch(this, "person", onReferenceChange); } private function setupBinding():void { BindingUtils.bindProperty(nti, 'text', person, 'name'); BindingUtils.bindProperty(ati, 'text', person, 'age'); } private function onReferenceChange(event:Event):void { setupBinding(); } } }
For comparison, let’s create the same component using mxml (which is much shorter) and call it DisplayPersonComponentMX.
<?xml version="1.0" encoding="utf-8"?> <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script> <![CDATA[ [Bindable] public var person:Person; ]]> </mx:Script> <mx:TextInput id="nti" text="{person.name}" /> <mx:TextInput id="ati" text="{person.age}" /> </mx:VBox>
Finally, let’s test it with an application.
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*"> <mx:Script> <![CDATA[ [Bindable] private var aperson:Person = new Person(); ]]> </mx:Script> <mx:Panel title="Component written in actionscript" width="300" height="100"> <local:DisplayPersonComponent person="{this.aperson}" /> </mx:Panel> <mx:Panel title="Component written in mxml" width="300" height="100"> <local:DisplayPersonComponentMX person="{this.aperson}" /> </mx:Panel> <mx:Button label="change person" click="aperson = new Person(Math.random().toString(), Math.random()*50);" /> <mx:Button label="change person's individual property" click="aperson.name = Math.random().toString(); aperson.age = Math.random()*50;" /> </mx:Application>
Thanks! Just what I was looking for!