jQuery UI Autocomplete with hybrid text/id search
Asked Answered
C

4

11

I'm having a lot of trouble making jQuery's autocomplete widget work for me. I am using a list of key/value pairs from a server.

I have the following requirements:

If the user selects a value from the widget, I want to pass the ID to the server.

If the user doesn't select a value and enters raw text, or modifies a value that has already been selected, I want the ID field to be cleared and just the raw text to be sent to the server.

Assume that someAjaxFunction returns an array of objects that the autocomplete widget expects: {label:label, value:key}.

Initially I set the autocomplete widget up like so:

$(input).autocomplete({
    source: sourceFunction,
    minLength: 1
});

Changing the selection even by hovering over one of the items changes the text in the text box referenced by $(input) to the underlying key, rather than the label. This is highly undesirable from a user interaction point of view - indeed, the very reason why I am investigating this is because the users of the site I am building were consistently baffled by the text they entered seemingly turning into random numbers!

I added a hidden field under the text box and implemented the select() and focus() events in order to cloak the ID like so:

$(input).autocomplete({
    source: sourceFunction,
    minLength: 1
    focus: function(event, ui) {
        $(idField).val(ui.item.value);
        $(this).val(ui.item.label);
        return false;
    },
    select: function(event, ui) {
        $(idField).val(ui.item.value);
        $(this).val(ui.item.label);
        return false;
    },
    minLength: 1
});

This works well when the user is sticking to the script provided by the autocomplete drop-down. The ID is cloaked and correctly submitted to the server. Unfortunately, if the user wants to enter some freeform text into the box and search based on that value, the ID field is not reset and the previously selected ID gets submitted to the server. This is also rather confusing.

The jQuery UI Autocomplete documentation lists a change event and states that the item property of the ui argument will be set to the selected item. I figured I could reset the hidden id field on a key press and re-populate the ID if the autocomplete is changed. Unfortunately, in addition to the keypress event capturing a whole bunch of keypresses that shouldn't reset the ID, the return false statement in the above select event which is necessary to manage the text in the text box prevents the change event from having ui.item properly assigned.

So now I'm stuck - I don't really know what else I can try to make the widget support functionality that it seems like it should support by default. Either this process is way more complicated than it should be, or I'm missing something really obvious. I've picked through all the available events and all the examples and come up empty handed. In fact, even the "Custom data and display" example on the jQuery UI page suffers from this problem.

I can add some hacks at the server side to cover for this, but I would really prefer to be able to do this at the client level.

I would also prefer to stick to the jQuery UI autocomplete widget rather than switch to another one.

Cheesy answered 21/7, 2010 at 13:40 Comment(2)
Sorry, can I clarify something? At least one of your problems is that you wish the ID box to be filled with either the associated ID if the user enters a value that has a corresponding ID, or whatever the user is typing if there is no corresponding value - correct/incorrect?Skep
Nah, I just want the hidden ID field to be empty if the text in the text box is set to something that doesn't correspond to a selection offered by the autocomplete.Cheesy
C
2

The way I'm doing autocomplete, is to create a container div that contains the input. When I select an element, I add a link containing a span to the container, and I hide the input box.

The link and span are styled so that it looks like a button, with an X, and has a click event handler to remove the entry from the DOM, clear the hidden field, and re-show the input box when clicked. So when an item is selected:

  <div class="container">
    <a href="#"><span>Selected Item</span></a>
    <input id="myautocomplete" type="text" />
    <input type="hidden" name="myvalue" value="1" />
  </div>

And when an item is not selected:

 <div class="container">
    <input id="myautocomplete" name="myautocomplete" type="text" />
    <input id="myvalue" name="myvalue" type="hidden" value="" />
  </div>

This way, you are either forced to select an item, or you enter free form text. On the backend, if the request parameter with key 'myvalue' is empty, then you would rather look at the request parameter with key 'myautocomplete'.

Connor answered 27/7, 2010 at 13:43 Comment(4)
also, in the select handler of the jQuery UI autocomplete, each time you select an item, you could create a hidden field with the same name, so if you're selecting multiple countries, make the hidden field's name attribute 'countries'. If you click on an entry to remove it, it removes the associated hidden field. On the server side, you end up retrieving a comma separated list of IDs that must be split and converted to an integer/long array.Connor
This is a great suggestion and in the absence of anything simpler, it's the way I'll go. Ideally though, I'd prefer something that requires a lot less building on top of the jQuery UI widget - it seems to me that the widget should support what I want to do more or less out of the box.Cheesy
Well I've been tasked with something similar so I was also looking for suggestions on how to do it well :) Glad my suggestion helped! Another thing is if one is using the same page for creation and editting - on reloading the page while editting, you have to recreate all the spans etc for the values in the hidden field. At the moment I'm just doing this on the server side. Only problem is that things like classes being used now must be maintained in the frontend code and backend code... I don't like it but it works.Connor
and if it worked out for you, don't forget to mark as an answer :)Connor
I
1

When i presume you have the following fields set:

<div>
    <input type="text" name="visible" class="autocomplete-reference" />
    <input type="hidden" name="hidden" />
</div>

You need to init following js (it works with jQuery 1.5.2):

$(document).ready(function() {
    $('.autocomplete-reference').each(function () {
        $(this).keydown(function(e){
            /*
            * this will reset hidden id reference on each visible field manual change
            * we have to bind it on keydown because we want to recognize event of submiting form by enter after autocomplete set
            */
            if (e.keyCode != 13) {
                $(this).siblings('input[type=hidden]').each(function () {
                    $(this).val('');
                });
            }
        });
        $(this).autocomplete({
            minLength: 1,
            /* you can set appending of autocomplete menu into some container to set css contextually
            appendTo: '#someElem',*/
            source: sourceFunction,
            search:function (event, ui) {
                //TODO ...spinner init...
            },
            open:function (event, ui) {
                /* you can grab autocomplete menu widget by this to manipulate some recognizing id, etc..
                 * $(this).autocomplete('widget')
                */
                //TODO ..stop spinner ...
                $(this).keydown(function(e){
                    /* this is important for live reference updates while arrow keys changes  */
                    if (e.keyCode == 37 || e.keyCode == 39) {
                       $($(this).data('autocomplete').menu.active).find('a').trigger('click');
                    }
                });
            },
            focus: function(event, ui) {
                /* here we'll map our data object onto both fields */
                $(this).val(ui.item.label);
                $(this).siblings('input[type=hidden]').each(function () {
                    $(this).val(ui.item.key);
                });
            },
            select: function(event, ui) {
                /* here we'll map our data object onto both fields */
                $(this).val(ui.item.label);
                $(this).siblings('input[type=hidden]').each(function () {
                    $(this).val(ui.item.key);
                });
            },
            close: function(event, ui) {
                //TODO ..stop spinner ...
            }
        });
    });
});

It's very nice to have some serverside validation which can convert exact :visible field values into references for quick typing folks of course.

Intermingle answered 1/1, 2012 at 13:38 Comment(0)
W
0

Though its an old question, my method can help.

On .change event you can search for the textbox value with the value in from your Source list using loop.

Goto for more details: Here it is in Ajax source list but workout is similar. Search a text In Jquery AutoComplete Ui

This thing had been troubling me many times and eventually now i figured it out :)

Whaley answered 7/12, 2012 at 7:4 Comment(0)
K
0

You can use event.preventDefault

Collect your selected values through ui.item.label or ui.item.value and get the needed value and set it to the same textbox.

var idNameCombo = ui.item.label;
            event.preventDefault();
            $("#mID").val(idNameCombo.split(",")[0].trim());
Koby answered 28/12, 2014 at 11:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.