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.

20 Responses to SelectedItem and ComboBox

  1. Per Olesen says:

    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!

  2. Jason says:

    Thanks Alex !!

  3. 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.

  4. Adnan Doric says:

    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.

  5. Adnan Doric says:

    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.

  6. 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.

  7. Dan McAdam says:

    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!

  8. pastis says:

    A bug has been submited to flex team for this issue. You can vote for it here : http://bugs.adobe.com/jira/browse/SDK-14751

  9. Dewey says:

    Hi Alex,
    First thank you for this code… I’ve used a version of it and it has simplified how we interact with ComboBoxes… mission accomplished.
    Now the issue: I’ve also read through your article on item renderers, specifically the “Custom Header Renderers (ComboBox)” one. I’m not able to get a merge of the two features working. I need to be able to specify the default/selected value for the Find version of the ComboxBox being used as/in a headerRenderer. Additionally, the selected value specifier can’t be hardcoded, it has to be dynamic… i.e., the selected value is the User Preference default, etc… It isn’t known until run time.
    I know it must feel like a simpleton question, but I can’t put your 2 examples together in a way that is working.
    Thanks for the learning.
    ——————–
    Alex responds:
    I’m not sure where the value you want to find is being stored, but it seems like you just need to wire this combo to it. You might get faster help asking on FlexCoders

  10. Joshua says:

    The problem is if the dataProvider from database. I can not make it. Do you have a sample code for dataProvider from database?
    ——————-
    Alex responds:
    There are examples in the doc. Ask on FlexCoders if you need more help

  11. Kyle Burke says:

    That you can’t preselect a drop down value without extending a core component is completely asinine. This is core functionality. Arg!

  12. elena says:

    That’s just wonderful ! Thank you Alex 🙂
    Most of the time when I find on the web some “easy” code it takes me about an hour to figure out how does it work, one more hour to try the example and another extra hour to apply that code on my own app.
    Your class it’s easy as copy-paste 🙂 you rock! I finally could get rid of that nasty loop… in less than 10 minutes!

  13. John says:

    Flex sucks, it shouldn’t be this hard to get and set items in a combobox.

  14. Jean-Christophe says:

    Seems that in late 2009, people still have problems using the ComboBox component.
    I wonder why the API does not define a dataField:String property, defaulting to value “data”, so that the ID property for the ComboBox items would be clearly defined ? (as done for labelField).
    That would make it easier to use the selectedItem setter, making everyone happy.
    Also, writing selectedItem={data:”id1″, label=”label 1″}, so that the object gets displayed in the combobox even though it does not exist in the dataProvider, is impossible.
    Of course, end users cannot select an object not present in the provider, but why developers cannot do it programmatically !?
    Subclassing the ComboBox can’t help, since the original property _selectedItem is private…
    Too bad i’m stuck, now.

  15. Jean-Christophe says:

    After a few tests, here is an awful hack which seems to do what i need :
    /**
    * This override allows the selection of an item, even if not present in the dataProvider.
    * CODE PROVIDED FOR INFORMATIONAL PURPOSES ONLY.
    * USE AT YOUR OWN RISKS.
    */
    override public function set selectedItem( item:Object ):void {
    super.selectedItem = item;
    // If not selected, force it :
    if( selectedItem==null && dataProvider ) {
    // Horrible hack that temporarily adds the item to the dataProvider so that it can be selected.
    // Modifying the dataProvider content must not generate any event that could provoke side-effects, so we use dataProvider.source.push() and .pop()
    var provider:ArrayCollection = dataProvider as ArrayCollection;
    provider.source.push(item);
    super.selectedItem = item;
    provider.source.pop();
    }
    }

  16. Alex Harui says:

    You are always welcome to file bugs, enhancement requests and patches.

  17. Synercoder says:

    I have a better solution. This uses Flex 4:
    [Bindable]
    private var countryDP:ArrayCollection = new ArrayCollection(
    [{show:”Andorra”},
    {show:”United Arab Emirates”},
    {show:”Afghanistan”},
    {show:”Antigua & Barbuda”},
    {show:”Anguilla”},
    {show:”Albania”},
    {show:”Armenia”},
    …..
    {show:”Zimbabwe”}]);
    private function selectItem(var s:String){
    var sort:Sort = new Sort();
    sort.fields = [new SortField(“show”, true)];
    countryDP.sort = sort;
    countryDP.refresh();
    var cursor:IViewCursor = countryDP.createCursor();
    var found:Boolean = cursor.findFirst({show: customer.country});
    country_field.selectedItem = cursor.current;
    }
    and the MXML:

  18. seema says:

    Hi adnan doric. I have a project in Flex CF and SQL. please email me seema.ghotra@gmail.com if interested.
    Thanks

  19. Peter says:

    hi Alex,
    I’m trying to do the same, but with an XMLListCollection as dataprovider… I try to adapt your code, went as long as the dataprovider is OK and displayed, but the item is not selected.. could you please provide some pointers as what might go wrong? (no error msg is displayed, I’m using Flash Builder and Flex4)
    thanks
    peter