Flex AutoComplete: New batch of changes

If you’ve come straight to this post I’d recommend checking out the previous posts for this component for more info on using it.

Latest version

Here’s what’s new:

  • Ability to add new items: You can now select items which aren’t in the data provider. To enable this feature you need to set the isStrict property to false. I’ve also added an allowDuplicates flag which controls whether or not you can add an item more than once
  • Added style property: I’ve added a style property which lets you control how the selected items are displayed. There are currently 3 choices: Mac Mail, Facebook and underlined.
  • Created some documentation: It’s not great but at least it’s a start. You can find it in the zip file in the doc folder
  • A bunch of other small tweaks and bug fixes: I’ve tried to incorporate the changes suggested in comments in the blog but I’m sure I missed some things. Please let me know if you suggested a change which didn’t make it in and I’ll get it in a future release.
  • Changed component name to AutoComplete: I’ve decided that for clarity’s sake it’s better to call it AutoComplete (rather than Chooser). I’m sorry for the refactoring this will require (hopefully it should be pretty minimal)

I’ve put the codebase on Google Code. If you’re interested in getting involved in the project please let me know.

Thanks,
Hillel

40 thoughts on “Flex AutoComplete: New batch of changes”

  1. Only hours after you post the latest code, and someone is already wanting to mocify it!

    The ‘flow” style multi-select seems to work well, and the auto height behavior will benefit many, but I want to be able to fix the height to a specific value and show a scrollbar if the content exceeds that height. I had done this with the previous version using a TextArea for the prompt input.

    I was looking at the code to see how I could do this with the current version. Hillel, what the heck is mx:ToolBar? It obviously exists, but I could not find it anywhere in the docs. Google finds it, but just a few usages, no documentation, and Flex
    Builder has no code completion for it.

    Do you have any suggestions on how to set its height? It takes the height property but seems to ignore it. Or are you adjusting its height programatically? I’m still looking.

    Tracy

  2. Ok, I can do what I need by jiggling the layout properties of the various components and containers. Now to see if I can make it an optional setting.

    1. Hey Tracy,

      I hadn’t heard of the ToolBar class either, It’s part of the RichTextEditor class. It’s marked as [ExcludeClass] which is why code completion doesn’t work on it. You can find out more about it here.

      Best,
      Hillel

  3. Hey Hillel,

    Just upgraded to the new version and I’m very excited about the facebook/mac styles!

    I’m going through the process of updating your component to meet all the use cases I have. Throughout the next couple days I’ll be posting my solutions in case you or anybody else wants to use them. Here is my first issue:

    Use Case: Suppose items A, B, and C are currently select. If ItemA is removed from the data provider then the AutoComplete component should automatically remove Item A and only display Items B and C.

    Problem: When the AutoComplete component is in multiSelect mode it doesn’t handle removals from the dataProvider correctly.

    Example: If you have items A, B, C, and D selected and remove item C from the data provider then you’ll wind up with items A, B, C, and D (2x) selected.

    Why: Behind the scenes it never removes your initial selections and that is why A, B, C, and D reamins selected. It tries to add “A B D” in rapid succession. However, each time it tries to add a new item it essnetially overwrites the previous item because the commitProperties method isn’t given enough time to run.

    Solution: Inside AutoComplete::commitProperties I changed the following code block:

    if (_selectedItemsChanged && _dataProvider)
    {
    _selectedItemsChanged = false;
    _selectedItems.removeAll();
    //Ed LaFave added this. Suppose ItemA was selected and it was removed from
    //the dataProvider. The handleDataProviderChange sets the selectedItems property
    //with all of the previously selected items except ItemA. If we don’t clear the
    //FlowBox then we wind up with duplicates.
    combo.clearFlowBox();

    for each (var selectedItem:Object in _initialSelectedItems)
    {
    var foundItem:Object = null;

    for each (item in _dataProvider.source)
    {
    if (item == selectedItem)
    {
    foundItem = item;
    }
    else if (item.hasOwnProperty( _keyField ) && item[_keyField] == selectedItem[_keyField])
    {
    foundItem = item;
    }

    if (foundItem)
    {
    combo.setSelectedItem( foundItem, true );
    //Ed LaFave added this. I had to validate now because the setSelectedItem
    //method was being called multiple times in rapid succession. Each call to
    //setSelectedItem essentially overwrote the previous call because the commitProperties
    //method didn’t get to run.
    combo.validateNow();
    foundItem = true;
    break;
    }
    }

    if (foundItem == null)
    {
    combo.setSelectedItem( selectedItem, true );
    //Ed LaFave added this. I had to validate now because the setSelectedItem
    //method was being called multiple times in rapid succession. Each call to
    //setSelectedItem essentially overwrote the previous call because the commitProperties
    //method didn’t get to run.
    combo.validateNow();
    }
    }
    }

    Thanks,
    Ed LaFave

  4. Hillel,

    Use Case: Suppose items A, B, and C are currently select. If ItemA is removed from the data provider then the AutoComplete component should automatically remove Item A and only display Items B and C.

    Problem: When the AutoComplete component is NOT in multiSelect mode it doesn’t remove the selected Item when it is removed from the data provider.

    Why: The Combo::commitProperties method didn’t account for the case where the selectedItem was null.

    Solution: I added the following code inside Combo::commitProperties:

    if (canBeAdded && value)
    {
    …BUNCH OF CODE HERE…
    }
    else if(_selectedItems.getItemIndex( value ) >= 0 && isNew)
    {
    …BUNCH OF CODE HERE…
    }
    else
    {
    validItem = false;

    //Ed LaFave added this. If we don’t remove the selected items
    //we’re not accounting for the case where value = null. If we
    //don’t account for this case then if the selected item is removed
    //from the data provider it continues to be displayed.
    clearFlowBox();
    _selectedItems.removeAll();
    }

    Thanks,
    Ed LaFave

  5. Hillel,

    Use Case: I basically have a global ArrayCollection that is used in several places throughout my app—most notably as the dataProvider for the AutoComplete objects I use. As one might expect it is only acceptable for the contents of this ArrayCollection to change when I specifically add or remove an item from that ArrayCollection.

    Problem: I use my global ArrayCollection as the dataProvider of the AutoComplete object. As the user uses the AutoComplete widgets the contents of my global ArrayCollection change even though I haven’t added/removed items from the ArrayCollection.

    Why: The AutoComplete class shares its dataProvider with the Combo class. As the user types in the AutoComplete widget the Combo class filters the dataProvider. Since the Combo class is operating on my “global” ArrayCollection it results in changes that I haven’t explicitly made.

    Solution: In the AutoComplete::commitProperties method I create a copy of the dataProvider and use that copy for Combo’s dataprovider:

    //Ed LaFave added this. The Combo class changes the contents of the dataProvider
    //in order to filter. My dataProvider is used in several places throughout my app
    //so it isn’t appropriate for that ArrayCollection to be filtered. Therefore we’ll
    //let the Combo class operate on its own ArrayCollection object.
    var dataProviderCopy:ArrayCollection = new ArrayCollection(_dataProvider.source);
    combo.dataProvider = dataProviderCopy;

    ps: I think that is the way you originally implemented this so you must have had a good reason for changing it. Hope I didn’t break something but everything seems to work the way I want it to.

    Thanks,
    Ed LaFave

  6. Hilel,

    Firstly I must admit AutoComplete is coming along nicely, it’s a very nice component.

    I’m having a slight issue with using it with a dynamic data provider. Perhaps I am doing this incorrectly but I found I had to make a few changes to get this working correctly.

    BTW to simulate a dynamic data provider just create a function to do a callLater to repopulate the dataprovider. I then bound this to Combo.SEARCH_CHANGE event and updated the data provider unless an item was selected.

    My changes as follows:
    1) In Combo.mxml at the end of function commitProperties add an extra call to handleChange(null, false).

    2) handleChange() add an extra argument called propogate. This stops the clearFlowBox() call and the extra event firing. To aid understanding see below:

    public function handleChange( event:Event, propogate:Boolean = true):void
    {
    if (selectedItem && !_isMultiSelect && propogate)
    {
    _selectedItems.removeAll();
    clearFlowBox();
    }

    sizeTextInput();

    if (!_dataProvider)
    {
    return;
    }
    _searchStr = textInput.text;

    //var start:Date = new Date();
    filterData();
    //var end:Date = new Date();
    //trace(“>> search took: ” + (end.getTime() – start.getTime()) + ” msecs” );

    if (propogate)
    {
    var event:Event = new Event( SEARCH_CHANGE );
    dispatchEvent( event );
    }

    I would be interested if others managed to use out of the box.

    Cheers,
    Guy.

  7. Hello; awesome component.

    Using isStrict=”false”, the user must press enter after entering text which does not appear in the list of options, or the value is not set on the field. Is there a way to “force set” the new text as the value of the field without the user having to remember to press enter before moving on to the next field? Perhaps this could be done when the field loses focus, or…?

    Thanks,
    Matthew

    1. Mathew,

      Thanks for the feedback. Yeah, that’s a good idea… I think on “focus out” is the way to go. I’ll add it to the next version. I’ve made a lot of changes in the latest release and plan to do a mini-update in a week or two.

      Best,
      Hillel

  8. Regarding the copy dataProvider yes/no question, if we leave it like it is no, without the copy, then we can still isolate the main data source from changes by making the copy *before* assigning the dataProvder to the component.
    Tracy

  9. Hi Hillel,

    Thanks for the hard work on this great component! You’ve incorporated a lot of new, cool features in this latest release.

    However, it seems to me it’s been a bit rushed and not tested thoroughly. For example, selectedItems seems to be fundamentally broken; it doesn’t work to select multiple items in your demo and I’m having a few other issues as well that may be symptoms of the same problem.

    Any chance you could make the earlier versions available such that users can revert to a stable version while bugs are being ironed out?

    1. Stiggler,

      I’m sorry to hear you’re having trouble with the latest version. If you could explain your issues in more detail I’d be happy to help you figure it ou. To test the selectedItems property in the demo I use the following line of code.

      <mx:List dataProvider=”{ autoComplete.selectedItems }” labelField=”name”/>

      Here’s a link to the previous version http://web.me.com/hillelcoren/Site/chooser-0.93.zip

  10. Hilel,

    it would also be nice if we could override the number of items to display, i.e. ROW_COUNT value in Combo.

    If items render quite large the pop up always displays to the top of the combo.

    Cheers,
    Guy.

  11. Hillel,

    Try selecting multiple items in the demo for the latest component version; you may select several items in the browsing window, but, unlike previous versions, only one of them will actually be selected on clicking OK.

    Other bugs I’ve come across so far, exclusive to the latest version, are:

    * You can make the component display several items at once simply by setting selectedItem several times; it doesn’t appear to be cleared in-between. This works regardless of what isMultiselect is set to.

    * If you type a string longer than the width of the component, that string now continues across the right boundary of the component instead of single line wrapping.

    Hope that helps.

  12. Additionally, AutoComplete’s selectedItem property is null when the component’s change event is fired following selecting an item in the Browse dialog.

    1. Stiggler,

      Thanks very much for finding these issues. I’ve resolved all of them (although for the case where the text is too long I’ve needed to show scrollbars as it will be difficult to implement line wrapping). If you’d like to make use of these changes you check out a copy of the codebase from the Google Code site

      Thanks,
      Hillel

  13. Nice work on getting those fixes in so quickly!

    The wrapping issue of course has to do with your use of the ToolBox (FlowBox), which I assume is used in order to display the styles you’ve incorporated. However, using those styles come with a price as the display width and height of the component are no longer enforced. In addition to breaking the right boundary on typing a long string, a selectedItem whose string length is greater than the width of the component will force the text input to display multiple lines instead of only displaying part of the item on a single line.

    Perhaps you could consider doing away with the ToolBox for a particular style that doesn’t necessarily need it (underline)? Having scrollbars in an input component isn’t desirable in a lot of cases.

    Thanks for the hard work.

    1. Stiggler,

      I definitely agree that scrollbars aren’t ideal but I think in most use cases it’s unlikely that a single value will be longer than the width of the entire component. I like your idea of not using the FlowBox for the underline style my only concern is the complexity this will add (and time it will take to implement). The style code is pretty simple right now as all selected items are treated the same (they’re just children of the FlowBox). To make the underlined items wrap they’d need to be text inside a TextArea (which is a whole different can of worms).

      Thanks very much for your input, I think it’s greatly helping in the development of the component.

      Best,
      Hillel

  14. Dear Hillel,

    I’ve dream of it, you’ve done it !
    Thanks for listening to our needs and delivering us so rapidly a new version.

  15. Great work.
    small bug to report. If you use browse to select a value in the sample using Browse, the color doesnt change.

  16. Anyone got a sample of dynamic data? I added an event listener to “textChange” but it doesnt seem to be working. getRoads is the function that retrieves the data from the server.

    heres my code

    private function onTypeTextChange():void
    {
    if (delayTimer != null && delayTimer.running) delayTimer.stop();
    // only search for inputs of length 3 or more
    if (roadsAC.text.length > 2)
    {
    delayTimer = new Timer(500,1);
    delayTimer.addEventListener(“timer”, getRoads);
    delayTimer.start();
    }
    }

    1. Prem,

      Thanks for spotting the bug with the demo, I’ll have it fixed in the next release. The issue you’re seeing is caused by another bug. To fix it in AutoComplete.mxml change line 18 from “textChange” to “searchChange” then use the searchChange handler in your application. In the next release I’m going to change this back to textChange.

      Sorry for the trouble this caused you,
      Hillel

  17. Thank you .
    That did the trick. I changed the event name and recompiled so its working now. I am having another small issue and was wondering if you could help me with that. Now my calls are working and the server side code is executing no problem. But as the searchtext changes and the dataprovider changes the height of the combobox is changing as well and sometimes I get stuck with just one row and no scrollbar even though there are 10s of records if not more than 100.

  18. Ok let me explain this a little more coz I think I can be a little more specific. If you narrow it down to one item and then “Backspace” (or delete) the last character the server call is made again but the combobox row_count doesnt change. I am looking through the code to see if I can find where I can fix this.

    1. Prem,

      I’d suggest adding a List component to your app which has it’s dataProvider set to the same dataProvider you’re using for the AutoComplete, this way you can keep an eye on what values are in it. I realize it may be difficult, but if you’re able to post sample code demonstrating the problem I’d be happy to take a look at it.

  19. Can anyone let me know, what are the ways in which data can be provided to Autocomplete class.

    Also help to do, if country names are provided as dataprovider for Autocomplete class,in output there should be a label called country, which shouldn’t be selectable. under the label country, country names should be displayed which should be selectable.kindly help me.

    1. Halflife Baby,

      You can pass either an ArrayCollection or an XMLListCollection to the dataProvider property.

      I’m sorry, I’m not sure that I understand your second question.

  20. Hi Hillel,
    Thank you. My second question is,
    If one Autocomplete class has list of country names to display. If i typed ‘A’ it should display the country names which starts by the letter ‘A’ under the label COUNTRY NAMES..If possible reply me with some examples.
    Kind regards,
    Halflife_baby.

    1. Halflife Baby,

      If your dataProvider is simply an arrayCollection of strings then it should just work. If however it’s an array of objects then you’ll want to set the labelField to the name of the property for the country name. I’d suggest checking out the examples folder in the zip file.

      1. Hi Hillel,
        Thanks a lot for your reply. Since i am a
        beginner in Flex, i need some sample codings
        for this AutoComplete class. Kindly help me
        with sample codings.Thanks a lot once again.
        Kind Regards,
        Halflife Baby.

  21. Hi Hillel,
    Again it’s me. I know to connect flex with HSQL DB using java. But while creating ‘Auto Suggest’using Autocomplete component for one Flex application i didn’t get the output.I have used remote object to pass the value to java from flex.Could you kindly help me to solve this?
    Regards,
    Halflife Baby.

      1. Hi Hillel,
        No that’s the problem. while running the application it’s not showing error and at
        the same time it’s not showing the result
        too.
        regards,
        Halflife Baby.

  22. Интересная статья, кстати автору хочу предложить установить от яндекс.денег полезную фишку на сайт “Дай рубль”. Я бы дал, так сказать на поддержание. 😉

Leave a comment