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;
Test file source
Helper class source for 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!
Posted by: Per Olesen | January 3, 2008 04:20 AM
Thanks Alex !!
Posted by: Jason | January 6, 2008 07:23 PM
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.
Posted by: Tom Van den Eynde | February 8, 2008 05:04 AM
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.
Posted by: Adnan Doric | February 22, 2008 09:08 AM
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.
Posted by: Adnan Doric | February 23, 2008 07:48 AM
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.
Posted by: Shanon McQuay | March 25, 2008 08:55 PM
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!
Posted by: Dan McAdam | April 24, 2008 06:17 PM