views:

179

answers:

2

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:

  1. if you start typing in the ComboBox and press enter when the desired item comes up the ComboBox will remain empty
  2. 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
  3. 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}"/>

A: 

So, I read your post, dear Fred, and get some ideas about binding, like this:

<mx:Binding destination="value" source="cbo.value"/>

Inside of your component.

Also I here Kyle explain his vision. And I see no problem to switch it to spark component, with that idea.

Let me know please, if it helps.

Thank you.

Eugene
Eugene thanks for the advice. I assume you mean doing something like this<fx:Binding source="myCB.selectedItem" destination="myRetVal"/>?Unfortunately that doesn't seem to do the trick... I will check out the link though.
fred august
OK, checked out the post - unfortunately I don't think that does the trick either... somehow the code above is missing its header, which is <s:MXAdvancedDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx">. Turns out that s:MXAdvancedDataGridItemRenderer implements both IDropInListItemRenderer and IFocusManagerComponent so I don't think that's the issue. Thank you though...
fred august
I'll try to reconfirm the this issue for ADG, because it works for DataGrid.
Eugene
A: 

Ok, after much tribulation, I found that:

  • overriding the method set data seems to fix issue 3 - it makes a lot of sense after reading a lot more of the standard adobe flex literature (which BTW is awesome). Something like this should do the trick

        override public function set data(value:Object):void 
        {
            myCB.selectedItem = value.ownerResource;
            myCB.validateNow();
        }
    
  • the standard behavior of a CB seems to be to go back to an old value (i.e. what normally happens if pressing ESC) if you're typing a name and then press enter. Not sure how to change that behavior, but

  • issues 1 and 2 were due to the fact that the original value had not been set, and thus defaulting to the previews value filled with an empty value.

fred august