The Flex DataGrid is an incredibly useful component however there is one feature that is strangely missing in AS3 which is a cellEdit event.  One would assume that a listener could be attached to the DataGrid which gets fired when a cell is edited – which is true.  You can listen to the itemEndEdit event, unfortunately itemEditEnd is fired before any of the controls or dataProvider are updated. You have access to the original cell value, but the newly entered value is difficult to obtain.

An article by Paul Robertson from Adobe explains the sequence of DataGrid events and provides two workarounds that allow you to get the old and new values when a cell is edited.  You can actually put his solution to work right away and get on with your day.

I wasn’t fully satisfied with this approach only because it seems weird that Adobe would make us jump through all these hoops just to get a common bit of data. Paul suggests that that DataProvider would be a good place to listen, but he didn’t find any suitable events. As it turns out, this is exactly the way to do it and I’ll provide example source code below. It actually makes more sense because the DataGrid is just a view and you could have multiple views of this same data throughout your application.  When changes occur, they’re handled centrally by the model. This also fits nicely with the current trend of using a model locator singleton and binding UI controls to it. If your UI is bound to the model and the model is listening for changes, the whole thing just works and you don’t really need any code to handle data updates.

The relevant classes in the Flex API are the CollectionEvent.COLLECTION_CHANGE event, which is fired on every change.  Updates will contain one or more PropertyChangeEvents which have properties “oldValue,” “newValue” and several other useful properties.

Example Code:

Download DataGridExample.zip

If you don’t want to download the project, the working parts of the code look something like this (note this won’t compile by itself):

<mx:Script>
<!--[CDATA[
 
[Bindable]
private var gridData:ArrayCollection = new ArrayCollection();
 
/** 
 * initialize the data source (fired by creationComplete in WindowedApplication)
 */
private function onCreationComplete():void
{
  // the listener will detect all changes made to the collection
  gridData.addEventListener(CollectionEvent.COLLECTION_CHANGE,onCollectionChange);		
 
  // the source of an ArrayCollection is just an array.  we can replace it with
  // whatever we want .  note that this does not cause the change event to fire
  gridData.source = Widget.getDummyData();
}
 
/** 
  *This is the event handler when any gridData data has been 
  * changed, regardless of what UI Control changed it 
  */
private function onCollectionChange(event:CollectionEvent):void
{
  if (event.kind == CollectionEventKind.UPDATE)
  {
    var propChangeEvent:PropertyChangeEvent = event.items[0] as PropertyChangeEvent;
    var existingItem:Widget = propChangeEvent.source as Widget;
    // TODO: update the database, web service, etc
  }
  else if (event.kind == CollectionEventKind.ADD)
  {
    var newItem:Widget = event.items[0] as Widget;
    // TODO: insert into the database, web service, etc.
  }
}
]]>
</mx:Script>
 
<!-- ###  Notice there is no listener attached to the grid ### -->
<mx:DataGrid id="dg1" editable="true" dataProvider="{gridData}" width="100%" height="100%">
</mx:DataGrid>

Thanks to Paul for getting my gears spinning. I’d appreciate any comments or links to similar example code.