Organizational Research By

Surprising Reserch Topic

how to implement mustmatch and selectfirst in jquery ui autocomplete


how to implement mustmatch and selectfirst in jquery ui autocomplete  using -'jquery,jquery-ui,autocomplete'

I recently migrated a few of my Autocomplete plugins from the one produced by bassistance to the jQuery UI autocomplete.

How can the "mustMatch" and "selectFirst" be implemented with just callbacks and other options without modifying the core autocomplete code itself?
    
asked Sep 24, 2015 by pradip.bhoge
0 votes
9 views



Related Hot Questions



Government Jobs Opening

answered Sep 24, 2015 by badhwar.rohit
0 votes

I found this question to be useful.

I thought I'd post up the code I'm now using (adapted from Esteban Feldman's answer).

I've added my own mustMatch option, and a CSS class to highlight the issue before resetting the textbox value.

       change: function (event, ui) {
          if (options.mustMatch) {
            var found = $('.ui-autocomplete li').text().search($(this).val());

            if (found < 0) {
              $(this).addClass('ui-autocomplete-nomatch').val('');
              $(this).delay(1500).removeClass('ui-autocomplete-nomatch', 500);
            }
          }
        }

CSS

.ui-autocomplete-nomatch { background: white url('../Images/AutocompleteError.gif') right center no-repeat; }
answered Sep 24, 2015 by r3tt
0 votes

I discovered one issue. While the suggestion list is active you can submit your form even if the value doesn't match the suggestion. To dissallow this I added:

$('form').submit(function() {
        if ($(".ui-autocomplete li:textEquals('" + $(this).val() + "')").size() == 0) {
            $(this).val('');
            $("span").text("Select a valid city").show();
            return false;
        }
});

This prevents the form from being submitted and displays a message.

answered Sep 24, 2015 by abhi
0 votes

The solution I've used to implement 'mustMatch':


answered Sep 24, 2015 by vibhorsingh
0 votes

This JQuery-UI official demo has mustMatch, amongst other cool stuff: http://jqueryui.com/demos/autocomplete/#combobox

I've updated it to add autoFill, and a few other things.

Javascript:



/* stolen from http://jqueryui.com/demos/autocomplete/#combobox
 *
 * and these options added.
 *
 * - autoFill (default: true):  select first value rather than clearing if there's a match
 *
 * - clearButton (default: true): add a "clear" button
 *
 * - adjustWidth (default: true): if true, will set the autocomplete width the same as
 *    the old select.  (requires jQuery 1.4.4 to work on IE8)
 *
 * - uiStyle (default: false): if true, will add classes so that the autocomplete input
 *    takes a jQuery-UI style
 */
(function( $ ) {
    $.widget( "ui.combobox", {
        options: {
            autoFill: true,
            clearButton: true,
            adjustWidth: true,
            uiStyle: false,
            selected: null,
        },
    _create: function() {
        var self = this,
          select = this.element.hide(),
          selected = select.children( ":selected" ),
          value = selected.val() ? selected.text() : "",
              found = false;
        var input = this.input = $( "" )
                .attr('title', '' + select.attr("title") + '')
        .insertAfter( select )
        .val( value )
        .autocomplete({
            delay: 0,
            minLength: 0,
            source: function( request, response ) {
                var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" );
                        var resp = select.children( "option" ).map(function() {
                    var text = $( this ).text();
                    if ( this.value && ( !request.term || matcher.test(text) ) )
                    return {
                        label: text.replace(
                        new RegExp(
                            "(?![^&;]+;)(?!]*)(" +
                            $.ui.autocomplete.escapeRegex(request.term) +
                            ")(?![^]*>)(?![^&;]+;)", "gi"
                        ), "$1" ),
                        value: text,
                        option: this
                    };
                });
                        found = resp.length > 0;
                response( resp );
            },
            select: function( event, ui ) {
                ui.item.option.selected = true;
                self._trigger( "selected", event, {
                    item: ui.item.option
                });
            },
            change: function( event, ui ) {
                if ( !ui.item ) {
                    var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( $(this).val() ) + "$", "i" ),
                    valid = false;
                    select.children( "option" ).each(function() {
                    if ( $( this ).text().match( matcher ) ) {
                        this.selected = valid = true;
                        return false;
                    }
                    });
                    if ( !valid || input.data("autocomplete").term=="" ) {
                    // set to first suggestion, unless blank or autoFill is turned off
                                var suggestion;
                                if(!self.options.autoFill || input.data("autocomplete").term=="") found=false;
                                if(found) {
                                    suggestion = jQuery(input.data("autocomplete").widget()).find("li:first")[0];
                                    var option = select.find("option[text="+suggestion.innerText+"]").attr('selected', true);
                                    $(this).val(suggestion.innerText);
                        input.data("autocomplete").term = suggestion.innerText;
                            self._trigger( "selected", event, { item: option[0] });
                                } else {
                                    suggestion={innerText: ''};
                                    select.find("option:selected").removeAttr("selected");
                                    $(this).val('');
                        input.data( "autocomplete" ).term = '';
                                    self._trigger( "selected", event, { item: null });
                                }
                    return found;
                    }
                }
            }
        });

            if( self.options.adjustWidth ) { input.width(select.width()); }

            if( self.options.uiStyle ) {
                input.addClass( "ui-widget ui-widget-content ui-corner-left" );
            }


        input.data( "autocomplete" )._renderItem = function( ul, item ) {
            return $( "" )
                .data( "item.autocomplete", item )
                .append( "" + item.label + "" )
                .appendTo( ul );
        };

        this.button = $( " " )
            .attr( "tabIndex", -1 )
            .attr( "title", "Show All Items" )
            .insertAfter( input )
            .button({
                icons: {
                primary: "ui-icon-triangle-1-s"
                },
                text: false
            })
            .removeClass( "ui-corner-all" )
            .addClass( "ui-corner-right ui-button-icon" )
            .click(function() {
                // close if already visible
                if ( input.autocomplete( "widget" ).is( ":visible" ) ) {
                input.autocomplete( "close" );
                return;
                }

                // work around a bug (likely same cause as #5265)
                $( this ).blur();

                // pass empty string as value to search for, displaying all results
                input.autocomplete( "search", "" );
                input.focus();
            });

            if( self.options.clearButton ) {
            this.clear_button = $( " " )
                .attr( "tabIndex", -1 )
                .attr( "title", "Clear Entry" )
                .insertAfter( input )
                .button({
                    icons: {
                    primary: "ui-icon-close"
                    },
                    text: false
                })
                .removeClass( "ui-corner-all" )
                .click(function(event, ui) {

                        select.find("option:selected").removeAttr("selected");
                        input.val( "" );
                    input.data( "autocomplete" ).term = "";
                        self._trigger( "selected", event, { item: null });

                    // work around a bug (likely same cause as #5265)
                    $( this ).blur();
                });
            }

    },

    destroy: function() {
        this.input.remove();
        this.button.remove();
        this.element.show();
        $.Widget.prototype.destroy.call( this );
    }
    });
})( jQuery );

CSS (.hjq-combobox is a wrapping span)

.hjq-combobox .ui-button { margin-left: -1px; }
.hjq-combobox .ui-button-icon-only .ui-button-text { padding: 0; }
.hjq-combobox button.ui-button-icon-only { width: 20px; }
.hjq-combobox .ui-autocomplete-input { margin-right: 0; }
.hjq-combobox {white-space: nowrap;}

Note: this code is being updated and maintained here: https://github.com/tablatom/hobo/blob/master/hobo_jquery_ui/vendor/assets/javascripts/combobox.js

answered Sep 24, 2015 by balvant maurya
0 votes

Maybe it's just because this is an old issue, but I found that the easiest solution is already there in the plugin, you just need to use the proper functions to access it.

This code will handle the cases when the autocomplete loses focus with an invalid value:

change: function(e, ui) {
    if (!ui.item) {
        $(this).val("");
    }
}

And this code, much like the original functionality from bassistance, will handle the cases when there are no matches while typing in the autocomplete:

response: function(e, ui) {
    if (ui.content.length == 0) {
        $(this).val("");
    }
}

This works well with either a static array source, or a JSON data source. Combined with the autoFocus: true option, it seems to do everything needed in an efficient manner.

The last case that you may want to handle is what to do when the ESCAPE key is pressed with an invalid value in the textbox. What I do is use the value of the first matched result. And this is how I do that...

First, declare a variable to hold the best match. Do this outside of your autocomplete plugin.

var bestMatch = "";

Then use the following option:

open: function(e, ui) {
    bestMatch = "";

    var acData = $(this).data('uiAutocomplete');
    acData.menu.element.find("A").each(function () {
        var me = $(this);

        if (me.parent().index() == 0) {
            bestMatch = me.text();
        }
    });
}

Lastly, add the following event to your autocomplete:

.on("keydown", function(e) {
    if (e.keyCode == 27)        // ESCAPE key
    {
        $(this).val(bestMatch);
    }
})

You can just as easily force the field to be empty when the escape key is pressed. All you have to do is set the value to an empty string when the key is pressed instead of the bestMatch variable (which isn't needed at all if you choose to empty the field).

answered Sep 24, 2015 by 20shahi
0 votes

I'm doing it a little differently, caching the results and clearing the text field if the number of results for a certain term is zero:


answered Sep 24, 2015 by deepak07.s
0 votes

Scott Gonzalez has written a selectFirst extension (as well as several others) for jQueryUI AutoComplete.

answered Sep 24, 2015 by okesh.badhiye
0 votes

Based on the accepted answer:

My additional requirements: multiple autocompletes, unobtrusive error validation.

change: function () {
    var target = $(this),
        widget = target.autocomplete('widget'),
        cond = widget.find('li:textEquals("' + target.val() + '")').length === 0;

    target.toggleClass('input-validation-error', cond);
}
answered Sep 24, 2015 by shikhar jain

...