« Threads in Actionscript 3 | Main | Flex and ScaleMode »

SelectedItem and ComboBox

Seems like it is once a month where someone is having trouble setting the selectedItem of a ComboBox. The key is that the ComboBox does an exact reference match on the items in the dataProvider. It doesnt take the time to compare values within the dataProvider items with the values in the item you are setting as the selectedItem.

In other words, if you have an array of objects:

[{ firstName: "Alex", lastName: "Harris" }, { firstname: "Alex", lastName: "Harui"}, {firstName: "Alex", lastName: "Trebek"}]
you can't just set:
selectedItem = { firstName: "Alex", lastName: "Harui }
as that assigns the selectedItem to a different Object instance with the same values. The ComboBox class will simply look to see if there is a reference to that instance in its dataProvider, which there is not. Instead, you have to scan the dataprovider and match up values.

So many folks are doing this, that I decided to post an example so we don't have to keep writing that scan-the-dataprovider loop. This example tries for an exact match first, so if you know you'll never have an exact match you can save more time by cutting out that first call to super.selectedItem = value;

Source for ComboBox subclass

Test file source
Helper class source for Test file

Test file

Usual caveats apply.

Comments

This one has hit me before, and that is one of the times, where it is great, that the sources for flex are open (so we can see exactly why our code is failing).

Nice solution, you got there!

Thanks Alex !!

Wouldn't it be better to have some kind of 'selectedValue' property? Because most of the time you don't have a completely similar object but just 1 property of the object (an ID or a name). It's actually sad to see that effort was put in an advanced datagrid but that the current set of controls hasn't been improved...

------------

File an enhancement request. It should be easy to subclass and add your own though.

maybe it is better to use :

if ( ObjectUtil.compare( obj, value ) == 0 )
{
super.selectedItem = obj;
return;
}

----------------

My example support partial matching where you might only know a few properties. You are correct that it doesn't support sub-object property matching, but ObjectUtil.compare is very slow so I'd only use it as a last resort.

If you have an object that you know is in the dataprovider, then a simple === test will be faster and work correctly.

In fact, the only reason ( for me ) to use ObjectUtils.compare instead of "for loop" is because in MXML code, it breaks code highlighting when you use something like this : selectedItem={ { id:myObject.id } }
that's why I prefer using : selectedItem={ myObject }

at least in Flex 2 plugin for eclipse.

I agree using ObjectUtils.compare might be a bit more concise.

In any case I would prefer to see a swappable comparator used so users can support their own logic for comparison.

public interface Comparator {
function compare (a:Object, b:Object): int;
}

For example I might want to compare on a unique id rather than the entire object.

-----------------------
Alex responds:

These examples are not supported or maintained. The source code is available so you can modify it as you please.

thanks for the nice example Alex!

I recently had a client that wanted to store a coded data element for the selection in a combo box, like this:











I'm using XML to return stored values to the Flex application from the MySQL DB and needed to set the selectedItem based on the stored data element... so your solution was just what I was looking for

However, I could not get it to work correctly in my situation at first:

I tried binding the return value in the MXML component directly like this:

or like this:

each of these solutions caused the combo box to show the first item as the selectedItem

so, I moved the setter to Actionscript:

b49.selectedItem={data:UserRecords.section.(@name =='binfo').q1};

with the same result.

I was about to abandon the effort and write a method to figure out the selectedIndex instead when I decided to try one more time. I spent some time going over and over your original code in the component and was puzzled by the test for a negative condition at:

if (obj[p] !== value[p])

and at:

if (!nope)

I removed the negative indicator (!) in both of those lines and compiled my application again, reading in values for the combo boxes, and viola!! it works like a champ.

I deployed the application today, using your extended ComboBox for 160 instances and am happy to report its working great! I included credits for your work in the code documentation and comments.... thanks again!

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)