Property Binding in Flex

I have been working more with Flex now, and I have to say: the data binding features(covered here) in Flex is beautiful. It's light years ahead of anything I've ever seen before. 


Example 1: You want a Text element to show the full name of current selected state from a drop down: 
    <List id="stateList" labelField="abrv">
        <ArrayCollection>
            <Object abrv="NJ" full="New Jersey"/>
            <Object abrv="MA" full="Massachusetts"/>
            <Object abrv="NY" full="New York"/>
            <Object abrv="NC" full="North Carolina"/>
        </ArrayCollection>
    </List>
    <Text text="{stateList.selectedItem.full}"/>
The code in the curly braces is some ActionScript code, but it doesn't just get executed once, oh no, you can look at it as an invariant, i.e. the content of the said text element will always contain the value expressed by stateList.selectedItem.full. Therefore, when you change the selection of stateList, the text element will immediately change with it. Here's the code and live demo for example 1.

Example 2: Let's try binding to a custom variable rather than to another control. We first make our counter variable public and bindable:
    <Script>
    [Bindable]
    public var counter:int = 0;
Then, we can bind a text control to it:
    <Text text="{counter}"/>
Next, we can periodically increment the variable like so:
        setInterval(function(){
            counter++;
        }, 1000);
And you will see the number in the text display count up: the display in the text element is syncronized with the actually value of counter. The code and live demo for example 2.
Next, let's try creating our own properties. ActionScript has support for Java-style OOP(as supposed to Javascript style), but, more interestingly, it's got direct support for properties. Add direct support of property change event bindings, and it's a beautiful thing. 
Example 3: You have a Page object that manages the details of paginating a set of data, and you need some controls to navigate between the pages as well as display what page you are on. You create a Page object:
    class Page extends EventDispatcher{
        public var number:int;
        public var size:int;
        public var total:int;
        ...
    }
A page has a page number, a page size, and the total size of the data set(used to tell whether a page exists). I instantiate a Page object:
    <Script>
    [Bindable]
    public var currentPage:Page = new Page(0, 10, 100);
    </Script>
I need a label that tells me the range of results currently being viewed:
    <Label text="{currentPage.from} to {currentPage.to}"/>
Here, the label binds to 2 properties: from and to. Let's implement these properties:
    class Page extends EventDispatcher{
        ...
        [Bindable]
        public function get from():int{
            return number * size + 1;
        }
        [Bindable]
        public function get to():int{
            return (number + 1) * size;
        }
        ...
    }
Notice the get syntax: this in ActionScript turns the function into a getter for a property of the same name. Also notice that I made these properties bindable.
Next, I need the previous and next buttons. I choose to sandwich the label in between them:
    <Button label="Previous" click="currentPage.prev()"/>
    <Label text="{currentPage.from} to {currentPage.to}"/>
    <Button label="Next" click="currentPage.next()"/>
Clicking on Previous will call the prev method and clicking on Next will call the next method. Simple. Here is the code:
        public function next(){
            number++;
        }
        
        public function prev(){
            number--;
        }
Compiled it, but it doesn't work. Clicking on Previous and Next does no change the display on the label at all. Oh! We need to fire the events! To fire a property change event, you'd do something like:
                this.dispatchEvent(new PropertyChangeEvent(
                    "propertyChange", true, true, 
                    PropertyChangeEventKind.UPDATE,
                    propertyName, null, null, this));
In our case, we are going to fire the change events for both from and to, because both of those must change after you flip a page. So we fire the changes:
        private function fireChanges(){
            var toFire = ['from', 'to'];
            for (var i = 0; i < toFire.length; i++)
                this.dispatchEvent(new PropertyChangeEvent(
                    "propertyChange", true, true, 
                    PropertyChangeEventKind.UPDATE,
                    toFire[i], null, null, this));
        }
        public function next(){
            number++;
            fireChanges();
        }
        public function prev(){
            number--;
            fireChanges();
        }
Ok, but I would like to disable the Previous or Next button when we are at the begining or the end of the dataset. No problem! We create a couple more bindable properties: hasNext and hasPrev:
        [Bindable]
        public function get hasNext():Boolean{
            return to < total;
        }
        [Bindable]
        public function get hasPrev():Boolean{
            return number > 0;
        }
Bind the buttons' enabled property to them:
    <Button label="Previous" click="currentPage.prev()" 
        enabled="{currentPage.hasPrev}"/>
    <Label text="{currentPage.from} to {currentPage.to}"/>
    <Button label="Next" click="currentPage.next()"
        enabled="{currentPage.hasNext}"/>
Make sure to fire the change events for them too when you are flipping pages(add them to toFire in the implementation of fireChanges) and we are done. The code: ex3.mxmlPage.as and live demo for example 3.
The end result of this is that you have very well separated MVC code. There isn't any code whose sole purpose is to sync the display like you get with jQuery code, for example. The code to do this is very minimal and naturally readable.
All code examples can be found here on Github.
Btw, I should mention that, the code inside of the curly braces does not allow any arbitrary ActionScript. There is some amount of flexibility, but not a lot. You can read more here.
blog comments powered by Disqus