Bindable combobox - selected item/index - apache-flex

I am trying to bind a datagrid item to a combox, so that when the user selects the item in the datagrid the form displays the selected data for editing, however one of my items is a combobox using a dataprovider.
I would like the item selected in the datagrid to match the selected item in the combobox, this part is fine however if my datagrid item is null then I cannot get the combox to set the selected index to -1?
(the same happens if you use the CRUD wizard in Flex builder 3 for ColdFusion)
I am using the following code for my custom combobox:
<mx:ComboBox
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
creationComplete="componentInit()"
>
<mx:Script>
<![CDATA[
import mx.utils.ObjectUtil;
import mx.controls.Alert;
[Bindable]
public var valueField:String = "";
[Bindable]
public var labelFields:Array = [];
public function componentInit():void {
this.labelFunction = renderLabelFunction;
}
public function renderLabelFunction(item:Object):String {
var result:String = "";
if (labelFields.length == 0) {
if (labelField != null) {
return item[labelField];
} else {
return item.toString();
}
}
else {
for(var i:int=0; i < labelFields.length; i++) {
if (i > 0) {
result += " ";
}
result += item[labelFields[i]];
}
}
return result;
}
override public function set selectedItem(val:Object):void {
//Alert.show(valueField +":" +ObjectUtil.toString(val));
if( this.valueField != null) {
for(var i:int=0; i < this.dataProvider.source.length; i++) {
var item:Object = this.dataProvider.source[i];
if ( item[valueField]== val ) {
// if it matches, make it selected.
this.selectedIndex = i;
break;
}
}
} else {
this.selectedIndex = -1;
}
}
public function get selectedItemValue():Object {
if( this.valueField != null && selectedItem != null) {
return selectedItem[valueField];
} else {
return text;
}
}
]]>
</mx:Script>
</mx:ComboBox>
and the MXML part calling the combox is:-
<mx:DataGrid id="clientDatagrid" selectedIndex="1" visible="true"/>
<mx:Form height="305">
<mx:FormItem direction="horizontal" label="Surname" required="true" visible="true" width="100%" horizontalAlign="left">
<mx:TextInput enabled="true" id="Surname" text="{clientDatagrid.selectedItem.Surname}" width="100%" visible="true"/>
</mx:FormItem>
<mx:FormItem direction="horizontal" label="Forename" required="true" visible="true" width="100%" horizontalAlign="left">
<mx:TextInput enabled="true" id="Forename" text="{clientDatagrid.selectedItem.Forename}" width="100%" visible="true"/>
</mx:FormItem>
<components:BindableComboBoxa id="gender"
dataProvider="{genderData}"
valueField="Code"
labelField="Description"
/>
</mx:form>
Any help would be much appreciated.
Thank you.

In selectedItem setter, testing this.valueField for nullity is useless because you set it to "Code" in the mxml. Instead you should test if val is null.
So just replace
if( this.valueField != null)
with
if( val != null)
and then it should work.

try setting a prompt for the combobox like this:
<components:BindableComboBoxa id="gender"
dataProvider="{genderData}"
valueField="Code"
labelField="Description"
prompt="Please Select"
/>

Related

Spark List in Flex mobile app: Pull down to refresh - with test code and screenshot

Trying to implement "pull down to refresh" I've created the following simple test code (please just add to a new Flash Builder project, with "blank" template, i.e. without navbar):
Screenshot:
TestPull.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
applicationComplete="init()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.PropertyChangeEvent;
private static const PADDING:uint = 20;
[Bindable]
private var _ac:ArrayCollection = new ArrayCollection();
private function init():void {
updateList();
_list.scroller.viewport.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handleScroll);
}
private function updateList():void {
_ac.source = new Array();
for (var i:int = 0; i < 42; i++) {
_ac.source.push(Math.random());
}
_ac.refresh();
}
private function handleScroll(e:PropertyChangeEvent):void {
if (e.source == e.target && e.property == "verticalScrollPosition") {
trace(e.property, ': ', e.oldValue, ' -> ', e.newValue);
if (e.newValue < -2 * PADDING &&
e.oldValue >= -2 * PADDING) {
_hint.visible = true;
setTimeout(hideHint, 2000);
//updateList();
}
}
}
private function hideHint():void {
_hint.visible = false;
}
]]>
</fx:Script>
<s:List id="_list"
dataProvider="{_ac}"
width="100%"
height="100%" />
<s:Label id="_hint"
text="Pull down to refresh..."
width="100%"
textAlign="center"
fontStyle="italic"
backgroundColor="#FFFFCC"
paddingTop="{PADDING}"
paddingBottom="{PADDING}"
visible="false" />
</s:Application>
This seems to work well and the _hint visibility is being toggled just once per pull (I've verified this with a trace).
However when I uncomment the updateList() call above (simulating data fetch from a web server) - everything breaks, the hint.visible=true is being set again and again and the _list is flickering.
Does anybody please have a suggestion, how to fix my poor man's pull to refresh?
I've ended up with this solution based on Michaël CHAIZE blog entry:
TestPull.mxml (add to a new Flex Mobile project):
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
applicationComplete="init()">
<fx:Declarations>
<s:ArrayCollection id="_ac"/>
<s:Fade id="_fadeIn" duration="500" alphaFrom="0" alphaTo="1" />
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.events.PropertyChangeEvent;
private static const PADDING:uint = 20;
private function init():void {
updateList();
_list.dataGroup.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handleScroll);
}
private function handleScroll(event:PropertyChangeEvent):void {
if (_busy.visible ||
event.source != event.target ||
event.property != 'verticalScrollPosition') {
return;
}
if (event.newValue < -3 * PADDING &&
event.oldValue >= -3 * PADDING) {
_hintDown.visible = true;
_hintUp.visible = false;
_fadeIn.play([_hintDown]);
} else if (event.newValue < -6 * PADDING &&
event.oldValue >= -6 * PADDING) {
_hintDown.visible = false;
_hintUp.visible = true;
_fadeIn.play([_hintUp]);
} else if (event.newValue >= -6 * PADDING &&
event.oldValue < -6 * PADDING) {
_hintDown.visible = true;
_hintUp.visible = false;
_fadeIn.play([_hintDown]);
} else if (event.newValue >= -3 * PADDING &&
event.oldValue < -3 * PADDING) {
_hintDown.visible = false;
_hintUp.visible = false;
}
}
private function startLoading(event:MouseEvent):void {
if (_hintUp.visible) {
_busy.includeInLayout = _busy.visible = true;
setTimeout(updateList, 5000);
}
_hintDown.visible = false;
_hintUp.visible = false;
}
private function updateList():void {
_ac.source = new Array();
for (var i:int = 0; i < 42; i++) {
_ac.source.push(Math.random());
}
_ac.refresh();
_busy.includeInLayout = _busy.visible = false;
}
]]>
</fx:Script>
<s:VGroup width="100%">
<s:HGroup id="_busy"
verticalAlign="baseline"
includeInLayout="false"
visible="false">
<s:BusyIndicator />
<s:Label text="Loading data..." />
</s:HGroup>
<s:List id="_list"
width="100%"
contentBackgroundColor="#FFFFFF"
dataProvider="{_ac}"
mouseUp="startLoading(event)" />
</s:VGroup>
<s:Label id="_hintDown"
text="↓ Pull down to refresh... ↓"
width="100%"
textAlign="center"
paddingTop="{PADDING}"
visible="false" />
<s:Label id="_hintUp"
text="↑ Release to refresh... ↑"
width="100%"
textAlign="center"
paddingTop="{PADDING}"
visible="false" />
</s:Application>

Change the visibility of itemRemderer

I have and add button (last row) in one column of the AdvancedDataGrid.
for the row having the add button the rest of fields are not visible.
On click of the add button a new row is added to the grid for the user to add.
After that this button becomes delete button (label becomes '-' and a new row is added at the bottom for adding another row).
When i click on the delete button (label '-'), the last row gets the add button('+' label) but the fields of the row become visible.
Can somebody please explain
Below is the sample code
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="400"
height="300"
initialize="group1_initializeHandler(event)">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.AdvancedDataGridEvent;
import mx.events.CollectionEvent;
import mx.events.DataGridEvent;
import mx.events.FlexEvent;
import mx.events.IndexChangedEvent;
import spark.events.IndexChangeEvent;
protected function group1_initializeHandler(event:FlexEvent):void
{
alarms=new ArrayCollection();
alarms.addItem(initalarmRow());
//alarms.addEventListener(CollectionEvent.COLLECTION_CHANGE, populateFieldDetails);
alarms.addItem(populateFieldforButton());
populateEvents();
}
private var _addButton:Boolean
[Bindable]
public function get addButton():Boolean
{
return _addButton;
}
public function set addButton(value:Boolean):void
{
_addButton=value;
}
private var _alarms:ArrayCollection;
[Bindable]
public function get alarms():ArrayCollection
{
return _alarms;
}
public function set alarms(value:ArrayCollection):void
{
_alarms=value;
}
private var _alarmRow:alarmVO;
[Bindable]
public function get alarmRow():alarmVO
{
return _alarmRow;
}
public function set alarmRow(value:alarmVO):void
{
_alarmRow=value;
}
// Initiliaze an alarmVO for a new row
private function initalarmRow():alarmVO
{
alarmRow=new alarmVO();
alarmRow.buttonLabel='-';
channels=new ArrayCollection;
for (var i:int=0; i < 10; i++)
{
var vo:ChannelVO=new ChannelVO();
vo.id=i;
vo.name="channel_" + i;
vo.messageType="Message_" + i;
channels.addItem(vo);
}
alarmRow.eventName="Event_1";
alarmRow.channel=channels.getItemAt(5) as ChannelVO;
return alarmRow;
}
private var _events:ArrayCollection;
[Bindable]
public function get events():ArrayCollection
{
return _events;
}
public function set events(value:ArrayCollection):void
{
_events=value;
}
private var _channels:ArrayCollection;
[Bindable]
public function get channels():ArrayCollection
{
return _channels;
}
public function set channels(value:ArrayCollection):void
{
_channels=value;
}
public function populateFieldforButton():alarmVO
{
alarmRow=new alarmVO();
alarmRow.buttonLabel="+";
return alarmRow;
}
public function populateEvents():void
{
events=new ArrayCollection();
for (var i:int=0; i < 3; i++)
{
var event:EventVO=new EventVO();
event.id=i;
event.eventName="Event_" + i;
events.addItem(event);
}
}
public function populateFieldDetails(event:Event):void
{
for (var count:int; count < alarms.length; count++)
{
//trace('alarms.getItemAt(count).buttonLabel :' + alarms.getItemAt(count).buttonLabel);
if (alarms.getItemAt(count).buttonLabel == '+')
{
alarms.getItemAt(count).channel=null;
alarms.getItemAt(count).eventName=null;
}
adgdalarmManagement.invalidateDisplayList();
}
}
public function preventEditing(event:AdvancedDataGridEvent):void
{
//check if it is the last row(it should not be editable)
if (event.rowIndex == alarms.length - 1)
{
event.preventDefault();
//trace('**** :' + event.currentTarget);
}
}
public function adgdalarmManagement_creationCompleteHandler(event:FlexEvent):void
{
}
protected function adgdalarmManagement_dataChangeHandler(event:FlexEvent):void
{
// TODO Auto-generated method stub
}
public function ddlEventType_creationCompleteHandler(event:FlexEvent, data:Object):void
{
for (var count:int=0; count < alarms.length; count++)
{
for (var count1:int=0; count1 < events.length; count1++)
{
if (events.getItemAt(count1).eventName == alarms.getItemAt(count).eventName)
{
event.currentTarget.selectedIndex=count1;
break;
}
}
}
checkEventTypeVisible(event, data);
}
public function checkEventTypeVisible(event:FlexEvent, data:Object):void
{
if (data == '-')
{
event.currentTarget.visible=true;
}
else
{
event.currentTarget.visible=false;
}
}
public function button1_clickHandler(event:MouseEvent):void
{
if (event.currentTarget.label == '-')
{
event.currentTarget.parent.parent.parent.parent.dataProvider.removeItemAt(event.currentTarget.parent.parent.parent.parent.selectedIndex);
event.currentTarget.parent.parent.parent.parent.dataProvider.refresh();
adgdalarmManagement.validateNow();
}
else
{
var selectedIndex:int=event.currentTarget.parent.parent.parent.parent.selectedIndex;
alarmRow=new alarmVO();
alarmRow.buttonLabel='-';
alarmRow.eventName="";
alarmRow.channel=new ChannelVO();
event.currentTarget.parent.parent.parent.parent.dataProvider.removeItemAt(event.currentTarget.parent.parent.parent.parent.selectedIndex);
event.currentTarget.parent.parent.parent.parent.dataProvider.addItemAt(alarmRow, selectedIndex);
event.currentTarget.parent.parent.parent.parent.dataProvider.addItemAt(populateFieldforButton(), selectedIndex + 1);
}
}
public function ddlChannel_changeHandler(event:IndexChangeEvent):void
{
event.target.parent.data.typeDisplay=event.target.selectedItem.name;
event.target.parent.data.messageTypeDisplay=event.target.selectedItem.messageType;
}
public function ddlChannel_creationCompleteHandler(event:FlexEvent, data:Object):void
{
// TODO Auto-generated method stub
if (channels != null)
{
if (alarms != null)
for (var count:int=0; count < alarms.length; count++)
{
for (var count1:int=0; count1 < channels.length; count1++)
{
if (alarms.getItemAt(count).channel != null)
{
if (channels.getItemAt(count1).name == alarms.getItemAt(count).channel.name)
{
event.currentTarget.selectedIndex=count1;
break;
}
}
else
{
event.currentTarget.selectedIndex=0;
}
}
}
}
else
{
event.currentTarget.selectedIndex=0;
}
checkVisible(event, data);
}
public function checkVisible(event:FlexEvent, data:Object):void
{
if (data == '-')
{
event.currentTarget.visible=true;
}
else
{
event.currentTarget.visible=false;
}
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:VGroup paddingTop="10"
paddingBottom="10"
paddingLeft="10"
paddingRight="10"
width="100%"
height="100%"
id="vbxChannelManagement">
<!-- Added for Rounding off Corners of GRID-->
<mx:ApplicationControlBar width="100%"
cornerRadius="8"
height="100%">
<mx:AdvancedDataGrid id="adgdalarmManagement"
width="100%"
height="100%"
dataProvider="{alarms}"
dataChange="adgdalarmManagement_dataChangeHandler(event)"
creationComplete="adgdalarmManagement_creationCompleteHandler(event)">
<mx:columns>
<mx:AdvancedDataGridColumn id="adgcAdRemove"
width="30"
dataField="buttonLabel">
<mx:itemRenderer>
<fx:Component>
<s:MXAdvancedDataGridItemRenderer>
<s:Button label="{data.buttonLabel}"
width="30"
click="outerDocument.button1_clickHandler(event)"/>
</s:MXAdvancedDataGridItemRenderer>
</fx:Component>
</mx:itemRenderer>
</mx:AdvancedDataGridColumn>
<mx:AdvancedDataGridColumn id="adgcEvent"
dataField="buttonLabel"
headerText="Event">
<mx:itemRenderer>
<fx:Component>
<s:MXAdvancedDataGridItemRenderer>
<s:HGroup id="eventGroup"
visible="{(this.parent.data.buttonLabel=='-')?true:false}}">
<s:DropDownList id="ddlEventType"
dataProvider="{outerDocument.events}"
creationComplete="outerDocument.ddlEventType_creationCompleteHandler(event,data.buttonLabel)"
labelField="eventName"/>
</s:HGroup>
</s:MXAdvancedDataGridItemRenderer>
</fx:Component>
</mx:itemRenderer>
</mx:AdvancedDataGridColumn>
<mx:AdvancedDataGridColumn id="adgcChannel"
dataField="buttonLabel"
headerText="Channel">
<mx:itemRenderer>
<fx:Component>
<s:MXAdvancedDataGridItemRenderer>
<!--<s:HGroup width="100%"
id="channelField"
>-->
<s:DropDownList id="ddlChannel"
width="100%"
dataProvider="{outerDocument.channels}"
creationComplete="outerDocument.ddlChannel_creationCompleteHandler(event,data.buttonLabel)"
labelField="id"
change="outerDocument.ddlChannel_changeHandler(event)"
/>
<!--</s:HGroup>-->
</s:MXAdvancedDataGridItemRenderer>
</fx:Component>
</mx:itemRenderer>
</mx:AdvancedDataGridColumn>
<mx:AdvancedDataGridColumn id="adgcChannelType"
headerText="Type"
dataField="typeDisplay"/>
<mx:AdvancedDataGridColumn id="adgcMessageType"
headerText="Message Type"
dataField="messageTypeDisplay"/>
</mx:columns>
</mx:AdvancedDataGrid>
</mx:ApplicationControlBar>
</s:VGroup>
</s:Group>
I got it working by overriding commitproperties function and in that function checking the viisbility of the dropdown.
something like
override public function commitproperties():void{
super.commitporperties();
if(data.buttonLabel == '-')
this.visible = false;
}

Passing multiple data items through checkbox

I have 2 checkbox repeaters: The first gets populated w/ an ArrayCollection & the second gets populated w/ the "Label" and "Data" fields selected from the first checkbox....how do I pass another item to the second checkbox? In the code below, I am currently only passing the "key" and "val" fields & would like to pass "another" field.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="updateOlder();"
>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import flash.events.MouseEvent;
import mx.controls.CheckBox;
import mx.core.Repeater;
[Bindable]
public var older:ArrayCollection = new ArrayCollection ([]);
[Bindable]
public var newer:ArrayCollection = new ArrayCollection ([]);
private function updateOlder():void{
older.addItem({key:'item1',val:{ name:'desc1' ,another:'another1'}});
older.addItem({key:'item2',val:{ name:'desc2' ,another:'another2'}});
older.addItem({key:'item3',val:{ name:'desc3' ,another:'another3'}});
}
private function updateNewer(evt:MouseEvent):void {
var tmpBox:CheckBox = evt.target as CheckBox;
if(tmpBox.selected) {
// add item to array
newer.addItem({key:tmpBox.label, val:tmpBox.data.val, another:tmpBox.data.another});
}
else {
// remove item from array
var newArrIndex: int = getArrayElementIndex(newer, older[tmpBox.instanceIndex]);
if(newArrIndex != -1){
newer.removeItemAt(newArrIndex);
}
}
newer.refresh();
}
private function getArrayElementIndex(arr:ArrayCollection, elementValue:Object):int{
for (var retInd: int = 0; retInd < arr.length; retInd++) {
if (arr[retInd]['key'] == elementValue['key'] && arr[retInd]['val']['name'] == elementValue['val']['name']
&& arr[retInd]['val']['another'] == elementValue['val']['another']) {
return retInd;
}
}
return -1;
}
]]>
</fx:Script>
<mx:HBox width="100%" height="100%">
<mx:VBox width="100%" height="100%">
<mx:Repeater id="oldRepeater" dataProvider="{older}">
<mx:CheckBox color="black" fontFamily="Arial" fontSize="14"
label="{oldRepeater.currentItem.key}"
data="{oldRepeater.currentItem.val}"
click="updateNewer(event);"
/>
</mx:Repeater>
</mx:VBox>
<mx:VBox width="100%" height="100%">
<mx:Repeater id="newRepeater" dataProvider="{newer}">
<mx:CheckBox color="red" fontFamily="Arial" fontSize="14"
label="{newRepeater.currentItem.key}"
data="{newRepeater.currentItem.val}"
/>
</mx:Repeater>
</mx:VBox>
</mx:HBox>
</mx:Application>
I'm not sure what exactly you want to accomplish, but it seems to me the easiest way to go is to make the Object you assign to the "val" field on each of the ArrayCollections contain both variables, i.e.
older.addItem({ key:'item1', val:{ name:'desc1' ,another:'another1'} });
and retrieve those values later:
private function getArrayElementIndex(arr:ArrayCollection, elementValue:Object):int{
for (var retInd: int = 0; retInd < arr.length; retInd++) {
if (arr[retInd]['key'] == elementValue['key'] && arr[retInd]['val']['name'] == elementValue['val']['name']
&& arr[retInd]['val']['another'] == elementValue['val']['another']) {
return retInd;
}
}
return -1;

Flex Datagrid dynamically adding rows via checkboxex

Does anybody know how to add a new row to a datagrid via a checkbox.
example:
checkbox 1 : label (PS2)
checkbox 2 : label (PS3)
checkbox 3 : label (PSP)
By selecting one or all of these checkboxes i what to add a new Datagrid row.
Datagrid
Console price
row1 PS2 $20,
row2 PS3 $30,
row3 PSP $15,
i hope this example is clear enough
thanks
DJ
Add an item to the dataProvider of the DataGrid from the change event handler of the CheckBox - make sure you check for existing items (and remove them when check box is unchecked) to avoid duplicates. If you can post the code of DataGrid, we might be able to give a sample code showing how to do this.
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="create()">
<mx:DataGrid id="dg" dataProvider="{dp}">
<mx:columns>
<mx:DataGridColumn dataField="console"/>
<mx:DataGridColumn dataField="price"/>
</mx:columns>
</mx:DataGrid>
<mx:CheckBox id="cb1" change="onCheck(event)"/>
<mx:CheckBox id="cb2" change="onCheck(event)"/>
<mx:CheckBox id="cb3" change="onCheck(event)"/>
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
private var prices:Array = ["$20", "$30", "$15"];
private var labels:Array = ["PS1", "PS2", "PS3"];
private var checkBoxes:Array;
[Bindable]public var dp:ArrayCollection;
private function create():void
{
checkBoxes = [cb1, cb2, cb3];
for(var i:Number = 0; i < labels.length; i++)
CheckBox(checkBoxes[i]).label = labels[i];
dp = new ArrayCollection([]);
}
private function onCheck(event:Event):void
{
var cb:CheckBox = CheckBox(event.currentTarget);
var index:Number = indexOf(cb.label);
if(cb.selected && index == -1)
dp.addItem({console:cb.label,
price:prices[labels.indexOf(cb.label)]});
else if(!cb.selected && index != -1)
dp.removeItemAt(index);
}
private function indexOf(str:String):Number
{
for(var i:Number = 0; i < dp.length; i++)
{
var item:Object = dp.getItemAt(i);
if(item.console == str)
return i;
}
return -1;
}
]]>
</mx:Script>
</mx:Application>

Flex: Combobox loses is focus when data-provider updated?

It seems that ComboBoxes loose their selected item after their dataProvider updates, even if the same selected item is still in the dataProvider. They convert back to the first item being selected. Is there anyway to prevent this? So that if the same object is in the dataProvider it keeps the same object selected and only reverts back to the first index if the selected object is not in the updated dataProvider?
Thanks!
If the ComboBox looses its selected item, it means the dataProvider isn't updated - it is replaced. If you bind a ComboBox to an ArrayCollection and then add an item to the AC, The ComboBox is updated without loosing its selectedItem.
Sometimes you have to replace the dataProvider and in those cases, you have to listen for the updateComplete-event and reset the selectedItem. You can try this code:
<mx:Script>
<![CDATA[
import mx.controls.ComboBox;
import mx.events.ListEvent;
import mx.events.FlexEvent;
import mx.collections.ArrayCollection;
[Bindable]
private var dp:ArrayCollection = new ArrayCollection(["Item 1", "Item 2", "Item 3"]);
private var selectedItem:*;
private var dataProvider:*;
private function onChange(event:ListEvent):void {
selectedItem = (event.currentTarget as ComboBox).selectedItem;
}
private function onUpdateComplete(event:FlexEvent):void {
trace(event);
var cb:ComboBox = event.currentTarget as ComboBox;
if(dataProvider == null || cb.dataProvider != dataProvider) {
if(selectedItem != null && cb.selectedItem != selectedItem) cb.selectedItem = selectedItem;
if(cb.selectedIndex < 0) cb.selectedIndex = 0;
dataProvider = cb.dataProvider;
}
}
private function extendDP():void {
dp.addItem("Item " + (dp.length +1));
var ac:ArrayCollection = new ArrayCollection(dp.source);
dp = ac;
}
private function reduceDP():void {
dp.removeItemAt(dp.length -1);
var ac:ArrayCollection = new ArrayCollection(dp.source);
dp = ac;
}
]]>
</mx:Script>
<mx:VBox>
<mx:ComboBox dataProvider="{dp}" change="onChange(event)" updateComplete="onUpdateComplete(event)" />
<mx:Button label="Extend dp" click="extendDP()" />
<mx:Button label="Reduce dp" click="reduceDP()" />
</mx:VBox>
It creates a ComboBox and binds it to an ArrayCollection. The two buttons adds and removes items from the collection.
Well I was able to extend the ComboBox with this class, which just looks for the selected label and compares with labels from the new dataProvider. It seems to work, although kinda ad-hoc. I was hoping for a scalable solution.
package
{
import mx.controls.ComboBox;
import mx.collections.ArrayCollection;
public class SelectionKeepingComboBox extends ComboBox
{
public function SelectionKeepingComboBox()
{
super();
}
override public function set dataProvider(value:Object):void
{
var curSelectedLabel:String;
if(this.selectedItem)
{
curSelectedLabel = this.selectedLabel;
}
super.dataProvider = value;
if(curSelectedLabel == null)
{
return;
}
var dp:Array;
if(this.dataProvider is ArrayCollection)
{
dp = this.dataProvider.toArray();
}
else
{
dp = this.dataProvider as Array;
}
for(var i:uint = 0; i<dp.length; i++)
{
var obj:Object = dp[i];
var dpLabel:String = obj.label;
if(dpLabel == curSelectedLabel)
{
this.selectedItem = obj;
}
}
}
}
}

Resources