¿Cómo implementar “mustMatch” y “selectFirst” en jQuery UI Autocomplete?

Recientemente migré algunos de mis complementos de Autocompletar desde el producido por bassistance a la autocompletar jQuery UI .

¿Cómo se pueden implementar “mustMatch” y “selectFirst” con solo retrollamadas y otras opciones sin modificar el código de autocompletado central?

Creo que resolví ambas características …

Para facilitar las cosas, utilicé un selector personalizado común:

$.expr[':'].textEquals = function (a, i, m) { return $(a).text().match("^" + m[3] + "$"); }; 

El rest del código:

 $(function () { $("#tags").autocomplete({ source: '/get_my_data/', change: function (event, ui) { //if the value of the textbox does not match a suggestion, clear its value if ($(".ui-autocomplete li:textEquals('" + $(this).val() + "')").size() == 0) { $(this).val(''); } } }).live('keydown', function (e) { var keyCode = e.keyCode || e.which; //if TAB or RETURN is pressed and the text in the textbox does not match a suggestion, set the value of the textbox to the text of the first suggestion if((keyCode == 9 || keyCode == 13) && ($(".ui-autocomplete li:textEquals('" + $(this).val() + "')").size() == 0)) { $(this).val($(".ui-autocomplete li:visible:first").text()); } }); }); 

Si alguna de sus sugerencias de autocompletar contiene caracteres ‘especiales’ utilizados por regexp, debe escapar de esos caracteres dentro de m [3] en el selector personalizado:

 function escape_regexp(text) { return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); } 

y cambie el selector personalizado:

 $.expr[':'].textEquals = function (a, i, m) { return $(a).text().match("^" + escape_regexp(m[3]) + "$"); }; 

Usé algo tan simple como esto para mustMatch y funciona. Espero que esto ayude a alguien.

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

Creo que conseguí que mustMatch trabajara con este código … aunque necesito una prueba:

  

Encontré que esta pregunta es útil.

Pensé en publicar el código que estoy usando (adaptado de la respuesta de Esteban Feldman ).

He agregado mi propia opción mustMatch y una clase CSS para resaltar el problema antes de restablecer el valor del cuadro de texto.

  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; } 

La solución que he usado para implementar ‘mustMatch’:

  

Descubrí un problema. Mientras la lista de sugerencias está activa, puede enviar su formulario incluso si el valor no coincide con la sugerencia. Para disculpar esto, agregué:

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

Esto evita que se envíe el formulario y muestra un mensaje.

Esta demo oficial de JQuery-UI tiene mustMatch, entre otras cosas interesantes: http://jqueryui.com/demos/autocomplete/#combobox

Lo he actualizado para agregar autocompletar y algunas otras cosas más.

Javascript:



 / * robado de http://jqueryui.com/demos/autocomplete/#combobox
  *
  * y estas opciones agregadas.
  *
  * - autocompletar (predeterminado: verdadero): seleccione el primer valor en lugar de borrar si hay una coincidencia
  *
  * - clearButton (predeterminado: verdadero): agregue un botón "borrar"
  *
  * - adjustWidth (predeterminado: true): si es verdadero, configurará el ancho de autocompletar igual que
  * el viejo seleccionar.  (requiere jQuery 1.4.4 para trabajar en IE8)
  *
  * - uiStyle (predeterminado: falso): si es verdadero, agregará clases para que la entrada de autocompletar
  * tiene un estilo jQuery-UI
  * /
 (función ($) {
     $ .widget ("ui.combobox", {
         opciones: {
             autoRellenar: verdadero,
             clearButton: cierto,
             adjustWidth: verdadero,
             uiStyle: falso,
             seleccionado: nulo,
         },
     _create: function () {
         var self = esto,
           select = this.element.hide (),
           selected = select.children (": selected"),
           value = selected.val ()?  selected.text (): "",
               found = falso;
         var input = this.input = $ ("")
                 .attr ('título', '' + seleccionar.attr ("título") + '')
         .insertAfter (seleccionar)
         .val (valor)
         .autocomplete ({
             retraso: 0,
             minLength: 0,
             fuente: función (solicitud, respuesta) {
                 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 (texto)))
                     regreso {
                         label: text.replace (
                         nuevo RegExp (
                             "(?! [^ &;] +;) (?!] *) (" +
                             $ .ui.autocomplete.escapeRegex (request.term) +
                             ") (?! [^] *>) (?! [^ &;] +;)", "gi"
                         ), " $ 1 "),
                         valor: texto,
                         opción: esto
                     };
                 });
                         found = resp.length> 0;
                 respuesta (resp);
             },
             select: function (event, ui) {
                 ui.item.option.selected = true;
                 self._trigger ("selected", event, {
                     item: ui.item.option
                 });
             },
             cambio: función (evento, ui) {
                 if (! ui.item) {
                     var matcher = new RegExp ("^" + $ .ui.autocomplete.escapeRegex ($ (this) .val ()) + "$", "i"),
                     válido = falso;
                     select.children ("option") .each (function () {
                     if ($ (this) .text (). match (matcher)) {
                         this.selected = valid = true;
                         falso retorno;
                     }
                     });
                     if (! valid || input.data ("autocompletar"). term == "") {
                     // configurado para la primera sugerencia, a menos que esté en blanco o que Autocompletar esté desactivado
                                 var sugerencia;
                                 if (! self.options.autoFill || input.data ("autocompletar"). term == "") found = false;
                                 si se encuentra) {
                                     suggestion = jQuery (input.data ("autocompletar"). widget ()). find ("li: first") [0];
                                     var option = select.find ("option [text =" + suggestion.innerText + "]"). attr ('selected', true);
                                     $ (this) .val (suggestion.innerText);
                         input.data ("autocompletar"). term = suggestion.innerText;
                             self._trigger ("selected", event, {item: option [0]});
                                 } else {
                                     suggestion = {innerText: ''};
                                     select.find ("opción: seleccionada"). removeAttr ("selected");
                                     $ (this) .val ('');
                         input.data ("autocompletar") .term = '';
                                     self._trigger ("selected", event, {item: null});
                                 }
                     regreso encontrado;
                     }
                 }
             }
         });

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

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


         input.data ("autocompletar") ._ renderItem = function (ul, item) {
             return $ (" 
  • ") .data ("item.autocomplete", artículo) .append ("" + item.label + "") .appendTo (ul); }; this.button = $ ("") .attr ("tabIndex", -1) .attr ("título", "Mostrar todos los elementos") .insertAfter (entrada) .botón({ íconos: { primario: "ui-icon-triangle-1-s" }, texto: falso }) .removeClass ("ui-corner-all") .addClass ("ui-corner-right ui-button-icon") .click (función () { // cerrar si ya está visible if (input.autocomplete ("widget") .is (": visible")) { input.autocomplete ("cerrar"); regreso; } // solucionamos un error (probablemente la misma causa que el # 5265) $ (esto) .blur (); // pasar cadena vacía como valor para buscar, mostrando todos los resultados input.autocomplete ("búsqueda", ""); input.focus (); }); if (self.options.clearButton) { this.clear_button = $ ("") .attr ("tabIndex", -1) .attr ("título", "Borrar entrada") .insertAfter (entrada) .botón({ íconos: { primario: "ui-icon-close" }, texto: falso }) .removeClass ("ui-corner-all") .click (función (evento, ui) { select.find ("opción: seleccionada"). removeAttr ("selected"); input.val (""); input.data ("autocompletar") .term = ""; self._trigger ("selected", event, {item: null}); // solucionamos un error (probablemente la misma causa que el # 5265) $ (esto) .blur (); }); } }, destroy: function () { this.input.remove (); this.button.remove (); this.element.show (); $ .Widget.prototype.destroy.call (esto); } }); }) (jQuery);

    CSS (.hjq-combobox es un intervalo de ajuste)

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

    Nota: este código se actualiza y mantiene aquí: https://github.com/tablatom/hobo/blob/master/hobo_jquery_ui/vendor/assets/javascripts/combobox.js

    Tal vez sea solo porque este es un problema antiguo, pero descubrí que la solución más fácil ya está allí en el complemento, solo necesita usar las funciones adecuadas para acceder a ella.

    Este código manejará los casos en que la autocompleta pierde el foco con un valor no válido:

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

    Y este código, al igual que la funcionalidad original de bassistance, manejará los casos cuando no haya coincidencias al escribir en el autocompletado:

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

    Esto funciona bien con una fuente de matriz estática o una fuente de datos JSON. Combinado con la opción autoFocus: true , parece hacer todo lo necesario de manera eficiente.

    El último caso que puede querer tratar es qué hacer cuando se presiona la tecla ESCAPE con un valor no válido en el cuadro de texto. Lo que hago es usar el valor del primer resultado coincidente. Y así es como lo hago …

    Primero, declare una variable para mantener la mejor coincidencia. Haga esto fuera de su complemento de autocompletar.

     var bestMatch = ""; 

    Luego usa la siguiente opción:

     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(); } }); } 

    Por último, agrega el siguiente evento a tu autocompletar:

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

    También puede forzar fácilmente que el campo esté vacío cuando se presiona la tecla de escape. Todo lo que tiene que hacer es establecer el valor en una cadena vacía cuando se presiona la tecla en lugar de la variable bestMatch (que no es necesaria en absoluto si elige vaciar el campo).

    Lo estoy haciendo de forma un poco diferente, almacenando en caché los resultados y borrando el campo de texto si el número de resultados para un cierto término es cero:

      

    Scott Gonzalez ha escrito una extensión selectFirst (así como varias otras) para jQueryUI AutoComplete.

    • Scott Gonzalez GitHub
    • SeleccionePrimera demostración

    Basado en la respuesta aceptada:

    Mis requisitos adicionales: autocompletas múltiples , validación de errores discretos .

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

    ¡Respuesta tardía pero podría ayudar a alguien!

    Teniendo en cuenta los dos eventos en el widget de autocompletar

    1) cambio: se activa cuando el campo está borroso y se cambia el valor.

    2) respuesta: se activa cuando finaliza la búsqueda y se muestra el menú.

    Modifique los eventos de cambio y respuesta de la siguiente manera:

     change : function(event,ui) { if(!ui.item){ $("selector").val(""); } }, response : function(event,ui){ if(ui.content.length==0){ $("selector").val(""); } } 

    ¡Espero que esto ayude!