One of the things I like most about ColdFusion's <CFSELECT> tag is the SELECTED attribute, give it a value and it figures out which one to pre-select. The Flex ComboBox allows you to set selectedIndex (the relative element position) but not selectedValue. Actually, ComboBox does have a selectedValue property, but that is used to read the value of the selected item, and can't be used to set the value of the item to be selected.
Coincidentally, while I was working on this, both Scott Stroz and Ray Camden posted solutions to the exact same problem. Scott's solution involved calling a method to set the value, and I really think that the selectedValue property should be used to be consistent with how ComboBox itself works. Ray's solution allows for a property to be set (he defined a new property for this), but his solution would not work for me as my dataProvider was being populated after the control had been created (it is being populated by a call to a CFC).
So, here is my solution. It's bit more complex than what Scott and Ray suggested, but it does support selectedValue, it'll also allow that property to be changed as needed (even after control creation), and it also properly handles delayed dataProvider population.
Here is the code:
<?xml version="1.0" encoding="utf-8"?>
<mx:ComboBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
private var _selectedValue:String;
private var bSelectedValueSet:Boolean = false;
private var bDataProviderSet:Boolean = false;
// Override committ, this may be called repeatedly
override protected function commitProperties():void
{
// invoke ComboBox version
super.commitProperties();
// If value set and have dataProvider
if (bSelectedValueSet && bDataProviderSet)
{
// Set flag to false so code won't be called until selectedValue is set again
bSelectedValueSet=false;
// Loop through dataProvider
for (var i:int=0;i<this.dataProvider.length;i++)
{
// Get this item's data
var item:String = this.dataProvider[i].data;
// Check if is selectedValue
if(item == _selectedValue)
{
// Yes, set selectedIndex
this.selectedIndex = i;
break;
}
}
}
}
// Trap dataProvider being set
override public function set dataProvider(o:Object):void
{
// invoke ComboBox version
super.dataProvider = o;
// This may get called before dataProvider is set, so make sure not null and has entries
if (o!=null && o.length)
{
// Got it, set flag
bDataProviderSet = true;
}
}
// set for selectedValue
public function set selectedValue(s:String):void
{
// Set flag
bSelectedValueSet = true;
// Save value
_selectedValue = s;
// Invalidate to force commit
invalidateProperties();
}
]]>
</mx:Script>
</mx:ComboBox>
And here is a simple test case:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:ns1="*">
<mx:Script>
<![CDATA[
[Bindable]
private var comboData:Array=
[{label:"Item1", data:"1"},
{label:"Item2", data:"2"},
{label:"Item3", data:"3"}];
]]>
</mx:Script>
<ns1:ComboBox2 dataProvider="{comboData}" selectedValue="3" />
</mx:Application>
Lots of us have needed a Flex ComboBox populated with U.S. States, and there are examples of that out there. But what I needed for an app I am working on is something a little different, a ComboBox that could display U.S. States or Canadian Provinces (but not at the same time, obviously). One simple (albeit ugly) solution would be to have two controls, switching the active one as needed. But nah, a better solution would be a control that does both. And so, with lots of invaluable help from Peter Ent, I created a USCAStates control. Here is how it could be used:
<!-- Display US States ComboBox -->
<ns1:USCAStates country="United States" />
<!-- Display CA Provinces ComboBox -->
<ns1:USCAStates country="Canada" />
And that country property can be change on the fly, forcing the list to refresh on demand based on whatever country name is passed. In my application I have another ComboBox containing countries. If United States is selected then the next ComboBox displays U.S. States, if Canada is selected then the next ComboBox displays Canadian Provinces, and if any other country is selected then the next ComboBox is disabled using enabled=false.
Here is the code for USCAStates:
<?xml version="1.0" encoding="utf-8"?>
<mx:ComboBox xmlns:mx="http://www.adobe.com/2006/mxml"
dataProvider="{internalDP}"
labelField="@label">
<mx:Script>
<![CDATA[
public static const USA:String = "United States";
public static const CANADA:String = "Canada";
// have the ComboBox look at this internal provider and switch it
// depending on the value of country
[Bindable]
private var internalDP:XMLList;
private var _country:String;
public function set country(c:String):void
{
_country = c;
if (_country==USA)
{
internalDP = usstates.state;
}
else if (country==CANADA)
{
internalDP = castates.state;
}
}
public function get country() : String
{
return _country; // just in case anyone wants to know
}
]]>
</mx:Script>
<mx:XML id="usstates" xmlns="">
<root>
<state label="Alabama" data="AL"/>
<state label="Alaska" data="AK"/>
<state label="Arizona" data="AZ"/>
<state label="Arkansas" data="AR"/>
<state label="California" data="CA"/>
<state label="Colorado" data="CO"/>
<state label="Connecticut" data="CT"/>
<state label="District of Columbia" data="DC"/>
<state label="Delaware" data="DE"/>
<state label="Florida" data="FL"/>
<state label="Georgia" data="GA"/>
<state label="Hawaii" data="HI"/>
<state label="Idaho" data="ID"/>
<state label="Illinois" data="IL"/>
<state label="Indiana" data="IN"/>
<state label="Iowa" data="IA"/>
<state label="Kansas" data="KS"/>
<state label="Kentucky" data="KY"/>
<state label="Louisiana" data="LA"/>
<state label="Maine" data="ME"/>
<state label="Maryland" data="MD"/>
<state label="Massachusetts" data="MA"/>
<state label="Michigan" data="MI"/>
<state label="Minnesota" data="MN"/>
<state label="Mississippi" data="MS"/>
<state label="Missouri" data="MO"/>
<state label="Montana" data="MT"/>
<state label="Nebraska" data="NE"/>
<state label="Nevada" data="NV"/>
<state label="New Hampshire" data="NH"/>
<state label="New Jersey" data="NJ"/>
<state label="New Mexico" data="NM"/>
<state label="New York" data="NY"/>
<state label="North Carolina" data="NC"/>
<state label="North Dakota" data="ND"/>
<state label="Ohio" data="OH"/>
<state label="Oklahoma" data="OK"/>
<state label="Oregon" data="OR"/>
<state label="Pennsylvania" data="PA"/>
<state label="Rhode Island" data="RI"/>
<state label="South Carolina" data="SC"/>
<state label="South Dakota" data="SD"/>
<state label="Tennessee" data="TN"/>
<state label="Texas" data="TX"/>
<state label="Utah" data="UT"/>
<state label="Vermont" data="VT"/>
<state label="Virginia" data="VA"/>
<state label="Washington" data="WA"/>
<state label="West Virginia" data="WV"/>
<state label="Wisconsin" data="WI"/>
<state label="Wyoming" data="WY"/>
</root>
</mx:XML>
<mx:XML id="castates" xmlns="">
<root>
<state label="Alberta" data="AB"/>
<state label="British Columbia" data="BC"/>
<state label="Manitoba" data="MB"/>
<state label="New Brunswick" data="NB"/>
<state label="Newfoundland" data="NL"/>
<state label="Northwest Territories" data="NT"/>
<state label="Nova Scotia" data="NS"/>
<state label="Nunavut" data="NU"/>
<state label="Ontario" data="ON"/>
<state label="Prince Edward Island" data="PE"/>
<state label="Quebec" data="QC"/>
<state label="Saskatchewan" data="SK"/>
<state label="Yukon" data="YT"/>
</root>
</mx:XML>
</mx:ComboBox>
[Update]
Here is a simple usage example, sent to me by Dan Zeitman:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:ns1="*">
<mx:RadioButtonGroup id="Country" change="{cbStates.country = Country.selectedValue.toString()}"/>
<mx:RadioButton label="United States" groupName="Country" selected="true"/>
<mx:RadioButton label="Canada" groupName="Country"/>
<ns1:USCAStates country="United States" id="cbStates" />
</mx:Application>