• Home
  • Products
  • Technology
  • Experience
  • Services
  • Our Company
  • Blog

Archives

  • February 2009
  • May 2008
  • April 2008
  • March 2008
  • December 2007
  • November 2007
  • October 2007
  • August 2007

Categories

  • Adobe AIR
  • Adobe Flex
  • Cairngorm
  • Encryption
  • General
  • Printing
  • UndoRedo Framework
  • Bookmarks

    • Adobe Labs
    • Planet Ubuntu

Log in
Data Binding in Flex, Part I

I’m just starting off with Flex and am enjoying some of the powerful things that it has available, among which is Data Binding.

Flex Data Binding is very powerful, and very convenient. The “About data binding” section in the Flex manual lists three different ways that we can use data binding:

  1. The curly braces, eg. ‘{‘ and ‘}’
  2. The <mx:Binding> tag, and
  3. The BindingUtils methods available in ActionScript

Let’s take a better look at the first approach. The first approach allows me to bind the result of a property, or chain of properties, or even functions to a destination property.

For example, let’s say I have a text area that I want to display a description. I could do something like the following:

...
[Bindable] public var myDescription:String;
...
<mx:textarea text="{myDescription}"></mx:textarea>

The above binds the myDescription property to the the text property of the text area. Luckily, we can do more complicated things. For example, I can bind to a property chain:

<mx:textarea text="{myObject.mySubObject.myDescription}"></mx:textarea>

In the above, if any of the properties along the property chain change, then the text property of the text area will be updated. Thus, if myObject changes (and it is a bindable property), then myObject.mySubObject.myDescription will be called and the result stored in the text area’s text property.

But Flex allows us to do even more than that. For example, I can reference a function as a part of the data binding:

<mx:textarea text="{myObj.mySingleton.getInstance().myProperty}"></mx:textarea>

In this case, if myObj changes, then the rest of the chain is re-evaluated, so mySingleton.getInstance().myProperty is called and the result stored as expected. Similarly, if mySingleton changes, then getInstance().myProperty is re-evaluated and its result stored.

But… how does data binding work? How does it know that something has changed and that all bound variables need to be re-evaluated?

Fortunately, we can find out how this works by looking at some of the generated code, so let’s take another look at the first example by adding the -compiler.keep-generated-actionscript flag:

[Bindable] public var myDescription:String;
...
<mx:textarea id="myTa" text="{myDescription}"></mx:textarea>

This will generate a file called _<FileName>-binding-generated.as that contains the following:

class BindableProperty {
    // [...snip...]
 
    [Bindable(event="propertyChanged")]
    public function get myDescription():String
    {
        return this._761273008myDescription;
    }
 
    public function set myDescription(value:String):void
    {
        var oldValue:Object = this._761273008myDescription;
        if (oldValue !== value)
        {
            this._761273008myDescription = value;
            dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent(
                this, "myDescription", oldValue, value
                );
        }
    }
    // [...snip...]
}

Thus, by default when we declare a public bindable property, without specifying the binding event, a getter and setter are created, with the actual underlying variable being a private variable prefixed by an odd generated number.Any time the value changes, the setter then dispatches a PropertyChangeEvent. If you call the setter with the same value, the event isn’t dispatched, saving precious processor time. Our example code also causes a file called _<FileName>BindingWatcherSetupUtil.as to be generated that watches for changes in myDescription. Without this code, although events would be dispatched, there wouldn’t be anything listening for the changes and making sure that anything else in our chain was re-evaluated. This generated file contains the following:

public class _DataBindingWatcherSetupUtil extends Sprite
{
    // ...
    public function setup(target:Object,
                            propertyGetter:Function,
                            bindings:Array,
                            watchers:Array):void
    {
        // ...
        watchers[1] = new mx.binding.PropertyWatcher("myDescription", {propertyChange:true} );
        watchers[1].addListener(bindings[0]);
        watchers[1].propertyGetter = propertyGetter;
        watchers[1].updateParent(target);
        bindings[0].uiComponentWatcher = 1;
        bindings[0].execute();
    }
}

In the above, setup() is called when by class is being initialized and is called as follows:

_watcherSetupUtil.setup(
    this,
    function(propertyName:String:* { return target[propertyName]; },
    _bindings,
    _watchers
    );

At this point in the code, _watchers is an empty array and _bindings was populated as follows:

var binding:Binding;
binding = new mx.binding.Binding(
    this,
    function():String
    {
        var result:* = (myDescription);
        var stringResult:String = (result == undefined ? null : String(result));
        return stringResult
    },
    function(_sourceFunctionReturnValue:String):void
    {
        myTa.text = _sourceFunctionReturnValue;
    },
    "myTa.text"
    );
_bindings[0] = binding;

Now, going back to the line that defines watchers[0]. watchers[0] is an undocumented class in the Flex Framework. As the source is provided, we can find out what it does. The PropertyWatcher constructor takes in a property name as its first parameter, and an object containing the event type that will be dispatched to indicate that a property has changed.

So, using the event dispatcher interface, the Property Watcher watches for the propertyChange event. When the propertyChange event comes in, it calls the execute method of the associated bindings which were registered in the setup() call, eg.:

watchers[1].addListener(bindings[0]);

Like the PropertyWatcher class, the Binding class is also undocumented. By that I mean that no public API documentation is available for it, although the code is commented and can be very useful. Binding.execute() then queries the myDescription getter using the anonymous function passed in as the second argument to the Binding constructor (see above). As long as execute can successfully execute the myDescription getter, it will then call the second anonymous funtion passed to the Binding constructor, thus setting the text property of my TextArea.

There are actually a few levels of indirection that happen, so I hope that in simplifying the above I haven’t missed anything or left out anything that is critical. Also, keep in mind this is the simplest version with a single property that was being monitored. Luckily, it doesn’t actually get much more complicated as the property chain being monitored gets longer.

As an example, consider the following:

<mx:textarea id="myTa" text="{myObj.mySubObj.myText}"></mx:textarea>

The _<FileName>BindingWatcherSetupUtil file will now contain something like the following:

watchers[1] = new mx.binding.PropertyWatcher("myObj", { propertyChange: true });
watchers[2] = new mx.binding.PropertyWatcher("mySubObj", { propertyChange: true });
watchers[3] = new mx.binding.PropertyWatcher("myText", { propertyChange: true });
watchers[1].addListener(bindings[0]);
watchers[1].propertyGetter = propertyGetter;
watchers[1].updateParent(target);
watchers[2].addListener(bindings[0]);
watchers[1].addChild(watchers[2]);
watchers[3].addListener(bindings[0]);
watchers[2].addChild(watchers[3]);
bindings[0].uiComponentWatcher = 1;
bindings[0].execute();

In the above three different watchers are created, one for each property. Each watcher monitors its property and, in the event that any of them change, bindings[0].execute() is called which calls the same anonymous functions that were shown in the simplified example above.

Ok, I’m stopping for now, but more to come later.

Posted by Kaleb Pederson on October 5, 2007 at 4:04 pm
2 Comments or LinksAdobe Flex
  1. 1
    Derek Basch
    April 28, 2008 at 11:26 am

    Greate post. Thanks!

  2. 2
    Ruth
    August 7, 2008 at 7:58 am

    Hey Kaleb,

    thanks for this post! I was wandering how the data binding looks like internally and now I now!! Now I understand the magic!

Incoming Links

Trackback this post | Subscribe to the comments via RSS Feed