Flex has the useful capability of binding a property of an object to another object or some function. Binding is usually done inline in mxml by binding to a value that has the bindable meta-tag set. Binding can also be done programatically using the BindingUtils class. This post will list some caveats with binding and show you how to bind programatically using the BindingUtils class.
Binding in mxml
First, have a look at a simple example of binding in mxml:
<!--mxml code-->
<s:Label text={controller.username} />
// Code inside Controller class:
// Option 1. the referenced value as a bindable variable
[Bindable]
public var username;
// Option 2. the referenced value as a bindable getter-setter
private var _username:String;
[Bindable]
public function get username():String {
return _username
}
public function set username(value:String):void {
_username = value;
}
Some notes:
Whether you choose to bind to a getter-setter or a simple variable, it’s up to you. Usually it is good practice to use getters & setters with binding. You can add a small optimisation by dispatching an event to trigger the binding:
// Option 2 Revised: binding on a specific event
private var _username:String;
[Bindable("usernameUpdated")]
public function get username():String {
return _username
}
public function set username(value:String):void {
if(_username != value) {
_username = value;
dispatchEvent(new Event("usernameUpdated"));
}
}
When one sets the [Binding] metadata tag on a property, an update to that property listens for all events. By specifying the event on which to bind – [Binding("usernameUpdated") - an update to that property listens for that specific event.
Setting up binding programmatically
Next, here is an example of binding programmatically using the BindingUtils class. In this scenario we have a class (SomeClassA) that wants to have its property (propertyA) and function (functionA) bound to the property (propertyB) of another class (SomeClassB). Taking this further, there is another class (SomeClassC) whose property (propertyC) will also be bound to a property (propertyB).
// Code in SomeClassA
public var someClassB:SomeClassB; // we will be binding to the values in this class
public var someClassC:SomeClassC; // a value in this class will be bound
public var propertyA:String; // a value that will be bound
public function functionA(value:String):void { // a function that will be bound
trace("The bound value is", value);
// do some stuff with 'value'
}
// Set up the binding
public function setupBinding():void {
BindingUtils.bindProperty(this, "propertyA", someClassB, "propertyB");
BindingUtils.bindSetter(functionA, someClassB, "propertyB");
BindingUtils.bindProperty(someClassC, "propertyC", someClassB, "propertyB");
}
...
// Code in SomeClassB
private var _propertyB:String;
[Bindable("propertyBUpdated")]
public function get propertyB():String {
return _propertyB;
}
public function set propertyB(value:String):void {
if(_propertyB != value) {
_propertyB = value;
dispatchEvent(new Event(“propertyBUpdated”));
}
}
...
// Code in SomeClassC
public var propertyC:String;
It is important to note that the property being bound to is made bindable. If not changes to the updated property do not get detected.
Just like with event listeners, you have options in the BindingUtils parameters to auto flag the binding listeners for garbage collection. The BindingUtils functions have an additional parameter 'useWeakReferences' which flags the binding listener for garbage collection once all it's references are null. You can also remove a binding listener manually. The BindingUtils functions return a ChangeWatcher object. If you set this initially when you create the binding, you can call the 'unwatch' method on it. Here is a revised version of our example above
// Set up the binding
private var watcher1:ChangeWatcher;
private var watcher2:ChangeWatcher;
private var watcher3:ChangeWatcher;
public function setupBinding():void {
watcher1 = BindingUtils.bindProperty(this, "propertyA", someClassB, "propertyB");
watcher2 = BindingUtils.bindSetter(functionA, someClassB, "propertyB");
watcher3 = BindingUtils.bindProperty(someClassC, "propertyC", someClassB, "propertyB");
}
// Call this when you are ready to remove binding
public function removeBinding():void {
watcher1.unwatch();
watcher2.unwatch();
watcher3.unwatch();
}
One more note on binding is to use it wisely. That is, with the knowledge of what is actually happening in the background when you set a [Bindable] meta tag or set it up programatically. To the novice Flex programmer it may appear that you are simply adding a single line of code, but what is actually happening is that a lot of auto-generated code gets created in the background. So your simple single line of code is actually a huge block of actionscript code. This is fine for small projects, but might cause unnecessary bloat in large projects. See more about it here:
http://www.slideshare.net/michael.labriola/diving-in-the-flex-data-binding-waters-presentation
Summary
1. Optimise your mxml bindings by dispatching an explicit event to trigger the binding
2. You can bind programatically using the BindingUtils.bindProperty or BindingUtils.bindSetter functions
3. When binding programatically make sure that the property you are binding to has been set to [Bindable]
4. You can improve your code by explicitly removing unnecessary binding listeners using 'unwatch()' of a ChageWatcher.
5. Understand that the using binding creates large blocks of actionscript code in the background that could cause unnecessary bloat in your project.