Tag Archives: binding

Lazy, Asynchronous Binding, or Call Once, Return Twice

The best part of binding is that it just works: your visible control (the target) gets the data (the source) to show without worrying about synchronizing it with the various steps of data retrieval.

“Getting the value” means retrieving a field, function, or accessor of an object. The target will always get the value when it is initialized in the Configuration phase (so const values work as binding sources even without the [Bindable] metatag).

The Bindable Mantra

“Hear the event, get the value”

The important and subtle implication of this is that the notification dispatcher and the (presumably changed) data are not necessarily the same thing. The normal and default behavior is that changing a value will dispatch an event to the targets bound to that value. This is the built-in behavior when properties use the [Bindable] metatag without specifying an event type; the compiler creates hidden accessors and mutators that checks for changed values (optimizing by ignoring new values that are the same as the old value) and dispatches a PROPERTY_CHANGED event.

The things that bind to the bindable property get a hidden event listener that listens for the PROPERTY_CHANGED event and assigns the source to the target whenever the listener gets an event.

Simple Binding

Target Object: a form

<Application />
    <s:List dataProvider="{foo.listData}" />
</Application>

Source Object: a data class (e.g. a model)

class Foo extends EventDispatcher
{
    [Bindable]
    public var listData : ICollection;
}

Specified Event Binding:[Bindable("foo")]

If the [Bindable] metatag specifies an event type (e.g [Bindable("event type")], then the compiler does not create hidden accessors and mutators for the property; the class must dispatch a notification event itself. One can use this to make non-accessor functions bindable: the hidden event listeners created for the targets will call the function whenever they hear the event and assign the result to the target.

A class can have many properties marked as [Bindable("foo")], and whenever it dispatches new Event("foo"), everything bound to any of those properties will retrieve the data value immediately.

Target Object: a form

<Application />
<!--   call get listData() on initialization and whenever "listDataChange" heard  -->
    <s:List dataProvider="{foo.listData}" />

<!--   call get listCount() on initialization and whenever "listDataChange" heard  -->
    <s:Label text="{foo.getListCount().toString()}" />   
</Application>

Source Object: a data class (e.g. a model)

class Foo extends EventDispatcher
{
    public static const LIST_DATA_CHANGE : String = "listDataChange";
    public static const TOTAL_COUNT_CHANGE : String = "totalCountChange";

    [Bindable("listDataChange")]
    public function get listData() : ICollection
    {
        return _listData;
    }

    public function setListData(value : ICollection) : void
    {
        if (_listData != value)
        {
            _listData = value;
            dispatchEvent(new Event(LIST_DATA_CHANGE));
        }
    }

    [Bindable("listDataChange")]
    public function getListCount() : int
    {
        if (_listData == null)
            return 0;
        else
            return _listData.length;
    }

    [Bindable("listDataChange")]
    [Bindable("totalCountChange")]    // something else dispatches this event
    public function totalCount() : int
    {
        return getListCount() + someOtherNumber;
    }
}

The Cool Bit

Because the notification can be separate from the actual data, one can return result asynchronously to a bound listener. This might be called the Data Accessor Object Pattern.

1. The target calls the source accessor
2. If the underlying object is uninitialized or stale

a. The accessor immediately returns null(which is a perfectly reasonable return value)

b. The accessor sends a request to a server

c. When the server returns data, the responder dispatches the binding event

d. The target hears the binding event and calls the accessor

3. The accessor returns the new data

Target Object: a form

<Application />
 <!--  call get listData() on initialization and whenever "listDataChange" heard -->
    <s:List dataProvider="{foo.listData}" />  
</Application>

Source Object: a data class (e.g. a model)

class Foo extends EventDispatcher
{
    public static const LIST_DATA_CHANGE : String = "listDataChange";

    [Bindable("listDataChange")]
    public function get listData() : ICollection
    {
        if (_listData == null)
        {
            var serverCall = new ServerCall(receiveListData);

            //  return null immediately; don't wait for the server
        }

        return _listData;
    }

    public function receiveListData(data : ICollection) : void
    {
        if (_listData != data)
        {
            _listData = data;

                     // make the bound targets call listData() AGAIN
            dispatchEvent(new Event(LIST_DATA_CHANGE));
        }
    }
}