UPDATE: I don’t actually follow this technique because, although I feel it is technically correct, nobody seems to do it and Adobe doesn’t really seem to fix related glitches in this approach (possibly because they don’t want you to do it this way). But anyway, read on if you are interested in an alternate approach to updating data in a DataGrid.
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:
UPDATE: Sorry, I deleted the code by accident during a server move! The gist of it is that you simply add a collection change watcher to your data collection (An array or whatever you are using as the dataProvider for the grid). When the user updates data in the grid, it will fire Collection Change Events, even though you don’t have a hander for itemEndEdit or anything. It’s actually a really interesting and clean way to handle it, however I have noticed that the grid fires multiple change events per action and I can’t figure out how to distinguish the “correct” event. The real problems is that sometimes you’ll get three events and only one of them will have values for oldValue and newValue, while the other two have nulls. Weird, huh? The events also fire differently depending on how you edit, for example if you hit enter it will fire events one way, but if you use tab it will fire them differently.
Basically, it works and I think it’s a great idea but I could not figure out the logic of why the redundant events came through and I couldn’t find any properties on them to indicate which one was the right event other than checking for nulls. If somebody knows the logic of the Collect Change events fired by the DataGrid, I’d definitely like to know about it. I haven’t read Adobe recommending this method anywhere so they probably are not looking into it. So for me I just went with the itemEndEdit event like we’re told to do. I cast event.currentTarget into the correct UIComponent in order to get the data. I think it’s a sloppy way to do it but that’s what all the examples suggest, so I’ve just done it that way in my own work.
Opinions and suggestions are still welcome.
One comment, in my source comments I mention that setting the “source” property doesn’t cause the change event to fire. Actually the change event does fire but the kind property is “reset” so it is ignored in this sample code. I’m too lazy to update the zip, but I wanted to clear that up.
Another thing to mention is that the change event seems to get thrown multiple times under different circumstances. If your data class is marked as [Bindable] (for example the Widget class) then the PropertyChangeEvent will contain oldValue and newValue. If not, they will both be null. Also the event gets fired twice, the 2nd time oldValue and newValue are null.
One glitch I’ve noticed is that if you sort by a column, then edit the values in that column, you’ll get inconsistent firing of the change event.
“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.”
I would recommend using itemEndEdit. The newly entered value is not difficult to get.
To get the new value use: event.currentTarget.itemEditorInstance
For example:
// This assumes that you’re using the default
// itemEditor which is a TextInput.
newValue:String = TextInput(event.currentTarget.itemEditorInstance).text
The Flex 3 Cookbook from O’Reilly has a number of good examples on how to use itemEndEdit.
Thanks for the comment Jesse – yea, itemEndEdit is what you see explained everywhere. I’m wondering if you have a personal reason for doing it that way? (I really mean that as an honest question, not as a challenge of some sort.)
Adding a listener to the collection has some odd behavior and I still question whether it’s the best way. But the reason I don’t like itemEndEdit is because you have to write a lot of code in the view – particularly in your event handler, you have to write some kind of switch statement to deal with all the columns unless they are all TextInput.
If you have all TextInput fields then its pretty easy but if you have a more typical grid with checkboxes, date fields, dropdowns, maybe some custom item renderers, then you have to know exactly what input control to expect and then cast the correct one in order to get the value. I don’t really like having to handle each column in the grid differently.
If you listen to the collection changes, on the other hand, the oldValue and newValue get passed to your already cast correctly and you don’t have to even know what type of input control was used to trigger the edit. I like dealing with things generically when I can so I don’t have to write a bunch of logic in the view layer.
Basically I think it’s a question of whether you want a smart view & dumb model, or a smart model & a dumb view. I’m taking the approach of a smart model because I’m lazy and I don’t like writing the same event handler code 10 times for the same data just because it appears in several places in the app.
I’d definitely be interested to hear what results people have gotten either way.
The “dataChange” event works great for me.
This was a huge help to me. I definitely favor monitoring the data model over the view. Does anyone know why the collection change event fires twice? Are people just ignoring propertyChangeEvents with null oldValue and newValue?
I can not download, the link may be gone.
crap! i moved servers a while back and stupidly deleted once i thought everything was transferred (which it obviously wasn’t) I’ll try to find the code but the gist of it is just to add a listener to your collection (ie the data provider) instead of the grid.
Then when you edit the grid it causes collection change events to fire – you don’t even have a listener on the grid, which is kinda cool.
However I don’t think this is a really common approach and as such Adobe doesn’t give it any attention. it seems to have some glitches, so just be warned.