Binding to Calculated Properties

A couple of weeks ago I posted about how I was using implicit getters in my model for calculated properties. This works great, but if you are calculating these values based on ArrayCollections you have to do a little extra coding to make sure changes to your ArrayCollection update your calculated value bindings.

Take, for example, the following application:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">

<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.charts.AreaChart;
import mx.events.CollectionEvent;
import mx.events.DataGridEvent;
import mx.collections.ArrayCollection;


[Bindable]
public var acmeData:ArrayCollection = new ArrayCollection(
[
{product: "Adding Machine", count: 5 },
{product: "All-Purpose Farm Implement", count: 1},
{product: "Anti-Nightmare Machine", count: 7},
{product: "Hi-Speed Tonic", count: 4},
{product: "Water Pistol", count: 7}
]);


[Bindable]
public function get total():int {
var total:int = 0;

for each(var item:Object in acmeData) {
total += Number(item.count);
}

return total;
}

]]>
</mx:Script>

<mx:VBox>

<mx:DataGrid id="myDataGrid" dataProvider="{acmeData}" editable="true" >
<mx:columns>
<mx:DataGridColumn dataField="product" editable="false" />
<mx:DataGridColumn dataField="count" />
</mx:columns>
</mx:DataGrid>

<mx:HBox>
<mx:Label text="Total:" />
<mx:Text text="{total}" />
</mx:HBox>

</mx:VBox>


</mx:Application>

The model has an ArrayCollection and an implicit getter that calculates the total for a column in the array collection. Both of these properties are bindable, however if you run the above example you will notice that changes to product counts of the ArrayCollection via the DataGrid do not cause the total to update. The problem is that this exmple binds to an ArrayCollection object and while calls to functions such as addItem() or removeItem() will trigger data binding, updates to properties of the ArrayCollection's composite objects will not. To work around this we need to listen for changes to the ArrayCollection and use some additional metadata to tell Flex when it should trigger data binding on our calculated property. Below is the updated code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">

<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.charts.AreaChart;
import mx.events.CollectionEvent;
import mx.events.DataGridEvent;
import mx.collections.ArrayCollection;

[Event (name="acmeDataChanged", type="flash.events.Event")]

[Bindable]
public var acmeData:ArrayCollection = new ArrayCollection(
[
{product: "Adding Machine", count: 5 },
{product: "All-Purpose Farm Implement", count: 1},
{product: "Anti-Nightmare Machine", count: 7},
{product: "Hi-Speed Tonic", count: 4},
{product: "Water Pistol", count: 7}
]);

public function init():void {
acmeData.addEventListener(CollectionEvent.COLLECTION_CHANGE, announceAcmeDataChange);
}

public function announceAcmeDataChange(e:Event):void {
dispatchEvent( new Event("acmeDataChanged"));
}

[Bindable (event="acmeDataChanged")]
public function get total():int {
var total:int = 0;

for each(var item:Object in acmeData) {
total += Number(item.count);
}

return total;
}

]]>
</mx:Script>

<mx:VBox>

<mx:DataGrid id="myDataGrid" dataProvider="{acmeData}" editable="true" >
<mx:columns>
<mx:DataGridColumn dataField="product" editable="false" />
<mx:DataGridColumn dataField="count" />
</mx:columns>
</mx:DataGrid>

<mx:HBox>
<mx:Label text="Total:" />
<mx:Text text="{total}" />
</mx:HBox>

</mx:VBox>


</mx:Application>

First we set up a listener on application start up so that we know when the acmeData ArrayCollection has changed. When this happens we announce a new event, acmeDataChanged, and supply this event name to the Binding metadata tag. Now when we run the application the total is updated anytime the count column in our DataGrid is updated.

So it may take a little more code to set calculated properties based on ArrayCollecions, but as I mentioned in my previous post, putting similar logic in the model and making it easily accessible via implicit getters makes for cleaner code.

Comments
BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.