Addons to eComboBox - Flex ComboBox selectedValue & More

A common requirement in the wonderful world of Flex is a combobox that enables us to set the selected state by value, instead of by the target's ordinal position. I'm sure that there are many implementations that are just a google search away, but the one that many of us have found was helpfully provided by Ben Forta. His implementation of a Flex combobox with selectedValue support is a good beginning and, in many cases, the end-all-be-all of what one might need.

However, there are those times when you need a little something extra. In particular, I found myself needing to the following:

  1. Using the standard ListEvent.CHANGE event to react to changes caused by setting the selectedValue
  2. Specifying the target property of the dataProvider on which selectedValue is compared.
  3. Using an XML dataProvider

So, taking a look at the original code from Ben:

<?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].value;

// 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>

Generating a ListEvent Change Event

Starting with catching the event, we need to throw the ListEvent.CHANGE when we commitProperties. This involves importing the mx.events.ListEvent class and the dispatching an event when the selectedValue is set:

<?xml version="1.0" encoding="utf-8"?><mx:ComboBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.events.ListEvent; // IMPORTING LIBRARY HERE

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].value;

// Check if is selectedValue
if(item == _selectedValue)
{
// Yes, set selectedIndex
this.selectedIndex = i;
dispatchEvent(new ListEvent(ListEvent.CHANGE)); // THROWING EVENT HERE
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>

This allows us to use the regular "change" event handler in the MXML:

<mx:Script>
<![CDATA[

private function myComboBoxEventHandler(e:ListEvent):void {
// do something
}

]]>

</mx:Script>

<ns1:eComboBox id="myEComboBox" dataProvider="{someDataModelVariaqble}" change="myComboBoxEventHandler(event)" />

Setting The selectedValue Property

The next mod that I had to make was the ability to specify the property on the dataProvider that should be used for the selectedValue comparison. Essentially, it requires that an additional property be set before specifying the selectedValue property. Essentially, this means adding a new private property, a setter for it, and then modifying the compare to take advantage of it. So building on the previously modified combobox we get:

<?xml version="1.0" encoding="utf-8"?><mx:ComboBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.events.ListEvent;

private var _selectedValue:String;
private var bSelectedValueSet:Boolean = false;
private var bDataProviderSet:Boolean = false;

private var _dataProviderValueField:String = "value"; // NEW PRIVATE VAR TO SPECIFY THE DATAPROVIDER PROPERTY

// 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][_dataProviderValueField].toString();;


// Check if is selectedValue
if(item == _selectedValue)
{
// Yes, set selectedIndex
this.selectedIndex = i;
dispatchEvent(new ListEvent(ListEvent.CHANGE));
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;
}
}

// SETTER FOR THE DATAPROVIDER PROEPRTY
public function set dataProviderValueField(value:String):void
{
_dataProviderValueField = value;    
}

// 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>

The default property is set, to "value" in this case, so you wouldn't necessarily have to explicitely set the property in all case, but you do get a bit of functionality with this minor mod.

Using XML as the dataProvider for the eCombobox

The last modification for this component, at least in this post, is the ability to use XML as a dataProvider. I was essentially forced to add this bit because I wanted to use Alex's flex US States combobox and he was on an XML kick when he built it - I was too lazy at the time to convert all of the dataProviders to ArrayCollections as it was a lot easier to just add the XML support.

Moving right along, to get the XML support, we need an additional Boolean property to keep track of what sort of data model we have, a setter for it, and then a quick mod to the compare statement for the selectedValue. Oh, and a quick note, my requirements were to match to an attribute. So the final bit of code:

.

<?xml version="1.0" encoding="utf-8"?><mx:ComboBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.events.ListEvent;

private var _selectedValue:String;
private var bSelectedValueSet:Boolean = false;
private var bDataProviderSet:Boolean = false;

private var _dataProviderValueField:String = "value";
private var _dataProviderIsXML:Boolean = false; // XML CONTROL VARIABLE

// 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
// UPDATED COMPARE STATEMENT
var item:String;
if (_dataProviderIsXML)
{
item = XML(this.dataProvider[i])..attribute(_dataProviderValueField).toString();
}
else {
item = this.dataProvider[i][_dataProviderValueField].toString();
}

var item:String = this.dataProvider[i][_dataProviderValueField].toString();;


// Check if is selectedValue
if(item == _selectedValue)
{
// Yes, set selectedIndex
this.selectedIndex = i;
dispatchEvent(new ListEvent(ListEvent.CHANGE));
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;
}
}

// Setter for the dataprovider proeprty
public function set dataProviderValueField(value:String):void
{
_dataProviderValueField = value;    
}

// SETTER FOR XML CONTROL VARIABLE        
public function set dataProviderIsXML(value:Boolean):void
{
_dataProviderIsXML = value;
}

// 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>

So the gist is that we keep the original compare statement, but prep it by setting the item based on the XML controll variable. So now we have an updated eComboBox that has some potentially useful modifications.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1.001. Contact Blog Owner