I would like to use a combobox as the itemEditor for one of the columns of an AdvancedDataGrid. I looked around and decided to use Spark ComboBox components - they're slick and seem to natively support a lot of neat stuff including auto-completing strings when types in the textInput. After some tinkering I managed to add a spark ComboBox to my ADG, setup its data provider in order for it to expand as the user adds new items, and hook it all up. The result seems promising, but as always, a few details have been impossible to overcome for me (indeed I'm not a flex pro quite yet) and was hoping that someone could point me in the right direction. The code is pasted below and uses the tricks described here to use a spark component as an itemEditor for an mx grid.
If you play with the (super simple) app you will notice that if you click (a couple of times) on the last column to the right and open the ComboBox you can pick a value from the list, click on a different cell, and the selected value will appear in the cell you just left. However, and here starts the list of annoying things:
- if you start typing in the ComboBox and press enter when the desired item comes up the ComboBox will remain empty
- if you type a new item in the ComboBox and press enter the item gets added to the data provider (correct behavior) but the ComboBox still remains empty
- when you first click on a cell its current value also disappears
I debugged the first two annoying scenarios and it seems that pressing Enter leads to advanceddatagrid1_itemEditEndHandler being called before myCB_changeHandler, which means that myRetVal is never set when the dataGrid does its update on its cells. The opposite happens if one selects from the list, i.e. the case that works. Not sure what to do about it though :-(
Would also love advice on the third annoying symptom.
thank you!
f
The MXML App
<fx:Declarations>
<s:ArrayCollection id="myCbDb"/>
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.AdvancedDataGridEvent;
import mx.events.DataGridEventReason;
import mx.events.FlexEvent;
[Bindable]
public var dataProv:ArrayCollection = new ArrayCollection();
[Bindable]
private var dpADG:ArrayCollection = new ArrayCollection([
{Name:'Pavement', duration:10, complete:0.1, ownerResource:'jon'},
{Name:'Pavement', duration:20, complete:.2, ownerResource:'randy'},
{Name:'Saner', duration:30, complete:.30, ownerResource:'joe'},
{Name:'Saner', duration:10, complete:.40, ownerResource:'mia'},
{Name:'The Doors', duration:5, complete:.50, ownerResource:'mia'},
{Name:'The Doors', duration:0, complete:.60, ownerResource:'jill'},
{Name:'Grateful Dead', duration:20, complete:.70, ownerResource:'jill'},
{Name:'Grateful Dead', duration:10, complete:.80, ownerResource:'joe'},
{Name:'Grateful Dead', duration:10, complete:.90, ownerResource:'jon'},
{Name:'The Doors', duration:5, complete:1, ownerResource:'jon'},
{Name:'The Doors', duration:10, complete:0, ownerResource:'jon'}
]);
private function formatDuration(data:Object, column:AdvancedDataGridColumn):String
{
var retVal:String = "";
var duration:Number = data[column.dataField] as Number;
retVal = duration.toString() + " days";
return retVal;
}
private function formatComplete(data:Object, column:AdvancedDataGridColumn):String
{
var retVal:String = "";
var duration:Number = data[column.dataField] as Number;
duration *= 100;
retVal = duration.toString() + " %";
return retVal;
}
private function formatResources(data:Object, column:AdvancedDataGridColumn):String
{
var retVal:String = data[column.dataField] as String;
return retVal;
}
protected function advanceddatagrid1_creationCompleteHandler(event:FlexEvent):void
{
for each (var a:Object in dpADG.source)
{
var s:String = a["ownerResource"];
if (!dataProv.contains(s))
{
dataProv.addItem(s);
trace("adding element ", s);
}
}
}
protected function advanceddatagrid1_itemEditEndHandler(event:AdvancedDataGridEvent):void
{
if (event.dataField == "ownerResource")
{
var editor:ResourceComboBoxField = ADG.itemEditorInstance as ResourceComboBoxField;
var name:String = editor.myRetVal;
// handle the ESC case first off
if (event.reason == DataGridEventReason.CANCELLED)
{
// Do not update cell.
return;
}
// do something with myRetVal if needed be
}
}
]]>
</fx:Script>
<mx:AdvancedDataGrid
width="100%" height="100%"
id="ADG"
sortExpertMode="true"
editable="true"
creationComplete="advanceddatagrid1_creationCompleteHandler(event)"
dataProvider="{dpADG}"
itemEditEnd="advanceddatagrid1_itemEditEndHandler(event)">
<mx:columns>
<mx:AdvancedDataGridColumn dataField="Name" />
<mx:AdvancedDataGridColumn dataField="duration" labelFunction="formatDuration" itemEditor="DurationField" editorDataField="value"/>
<mx:AdvancedDataGridColumn dataField="complete" labelFunction="formatComplete" itemEditor="CompleteField" editorDataField="value"/>
<mx:AdvancedDataGridColumn dataField="ownerResource" labelFunction="formatResources" editorDataField="myRetVal">
<mx:itemEditor>
<fx:Component>
<local:ResourceComboBoxField myDP="{outerDocument.dataProv}">
</local:ResourceComboBoxField>
</fx:Component>
</mx:itemEditor>
</mx:AdvancedDataGridColumn>
</mx:columns>
</mx:AdvancedDataGrid>
and the component
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.collections.IList;
import spark.events.IndexChangeEvent;
public var myRetVal:String = new String();
[Bindable]
public var myDP:ArrayCollection;
// Event handler to determine if the selected item is new.
protected function myCB_changeHandler(event:IndexChangeEvent):void
{
// Determine if the index specifies a new data item.
if(myCB.selectedIndex == spark.components.ComboBox.CUSTOM_SELECTED_ITEM)
// Add the new item to the data provider.
myDP.addItem(myCB.selectedItem);
myRetVal = myCB.selectedItem;
}
]]>
</fx:Script>
<s:ComboBox id="myCB" width="140" change="myCB_changeHandler(event);" dataProvider="{myDP}"/>