When Binding won’t work with your ArrayCollection
July 22nd, 2008I have no idea how much of my Flex career has been spent debugging why Bindings aren’t firing ‘correctly’ in a project, but I know I’ve banged my head against this specific problem a few times now.
A lot of the time I declare an ArrayCollection and whatever sort or filterFunction I need on it, and from that point forward I don’t want to reassign that reference to a new ArrayCollection or one returned from a service call because I don’t want to reapply the sort or FilterFunction.
myAC = new ArrayCollection();
var acSort:Sort = new Sort();
acSort.fields = [new SortField("name", true)];
myAc.sort = attSort;
If I detect a change on a data model or get some new data in a callback, I’ll swap out or modify the underlying Array in the ’source’ property of the ArrayCollection, and then call refresh() when I’m finished so any components bound to the ArrayCollection will be notified of the update
private function update(newData:ArrayCollection):void
{
myAC.source = newData.source;
myAC.refresh();
}
private function getText(ac:ArrayCollection):String
{
return ac.length.toString() + ‘ item(s)’;
}
<mx:Text id=”myText” text=”{getText(myAC)}” />
Great right? Sort and filterFunction are maintained, and components should update due to the refresh. Well, not exactly. If myAC is the dataProvider for a data driven component such as a List or DataGrid we’re in good shape, because when setting the dataProvider these components register for the COLLECTION_CHANGE event, which the refresh() will trigger. However, simple data-binding will NOT fire when this event is dispatched due to a refresh. There are many ways to work around this, one of which is to listen for the COLLECTION_CHANGE event on myAC when creating it and update component text, update variables, whatever you need to do in the listener. You could also keep a Bindable Boolean variable around and toggle it whenever data is updated, and then use that as a parameter to any functions that need to be called to update your components.
[Bindable] private var _dataUpdated:Boolean;
private function update(newData:ArrayCollection):void
{
myAC.source = newData.source;
myAC.refresh();
_dataUpdated = !_dataUpdated;
}
private function getText(updateFlag:Boolean):String
{
return myAC.length.toString() + ‘ item(s)’;
}
<mx:Text id=”myText” text=”{getText(_dataUpdated)}”/ >
In the example above the function ‘getText’ will be called whenever the _dataUpdated Boolean is toggled. This example is overly simplified because I could have just bound my text directly to the myAC.length property, which WILL work, but I wanted to illustrate that binding to the myAC reference will only fire if that reference is changed to a new ArrayCollection, NOT if refresh() is called.
There are probably at least a dozen other ways to work around this particular Binding issue, but there’s a quick one if you need it!