No detectar resultados en la función de autocompletar de jQuery UI

89

Antes de que me los señale, sí, he revisado la media docena de publicaciones sobre este tema, pero todavía estoy bloqueado en cuanto a por qué esto no funciona.

Mi objetivo es detectar cuándo el autocompletado produce 0 resultados. Aquí está el código:

 $.ajax({
   url:'sample_list.foo2',
   type: 'get',
   success: function(data, textStatus, XMLHttpRequest) {
      var suggestions=data.split(",");

  $("#entitySearch").autocomplete({ 
    source: suggestions,
    minLength: 3,
    select: function(e, ui) {  
     entityAdd(ui.item.value);
     },
    open: function(e, ui) { 
     console.log($(".ui-autocomplete li").size());
     },
    search: function(e,ui) {
     console.log("search returned: " + $(".ui-autocomplete li").size());

    },
    close: function(e,ui) {  
     console.log("on close" +  $(".ui-autocomplete li").size());    
     $("#entitySearch").val("");
    }
   }); 

  $("#entitySearch").autocomplete("result", function(event, data) {

   if (!data) { alert('nothing found!'); }

  })
 }
}); 

La búsqueda en sí funciona bien, puedo hacer que los resultados aparezcan sin problemas. Según tengo entendido, debería poder interceptar los resultados con el controlador de autocompletar ("resultado"). En este caso, nunca se dispara. (Incluso una alerta genérica o un archivo console.log que no hace referencia al número de resultados nunca se activa). El controlador de eventos abierto muestra el número correcto de resultados (cuando hay resultados), y los controladores de eventos de búsqueda y cierre informan un tamaño de resultado que siempre está un paso por detrás.

Siento que me estoy perdiendo algo obvio y deslumbrante aquí, pero simplemente no lo veo.

ScottyDont
fuente
Parece que no hay una manera fácil de lograr esto con un widget de autocompletar impulsado por datos del lado del cliente. ¿Es una opción usar una fuente remota para el widget?
Andrew Whitaker

Respuestas:

199

jQueryUI 1.9

jQueryUI 1.9 ha bendecido el widget de autocompletar con el responseevento, que podemos aprovechar para detectar si no se devolvieron resultados:

Se activa después de que se completa una búsqueda, antes de que se muestre el menú. Útil para la manipulación local de datos de sugerencias, donde no se requiere una devolución de llamada de opción de fuente personalizada. Este evento siempre se activa cuando se completa una búsqueda, incluso si el menú no se mostrará porque no hay resultados o la función Autocompletar está desactivada.

Entonces, con eso en mente, la piratería que tuvimos que hacer en jQueryUI 1.8 se reemplaza con:

$(function() {
    $("input").autocomplete({
        source: /* */,
        response: function(event, ui) {
            // ui.content is the array that's about to be sent to the response callback.
            if (ui.content.length === 0) {
                $("#empty-message").text("No results found");
            } else {
                $("#empty-message").empty();
            }
        }
    });
});​

Ejemplo: http://jsfiddle.net/andrewwhitaker/x5q6Q/


jQueryUI 1.8

No pude encontrar una manera sencilla de hacer esto con la API jQueryUI, sin embargo, puede reemplazar la autocomplete._responsefunción con la suya y luego llamar a la función jQueryUI predeterminada ( actualizada para extender el prototypeobjeto de autocompletar ) :

var __response = $.ui.autocomplete.prototype._response;
$.ui.autocomplete.prototype._response = function(content) {
    __response.apply(this, [content]);
    this.element.trigger("autocompletesearchcomplete", [content]);
};

Y luego vincule un controlador de autocompletesearchcompleteeventos al evento (el contenido es el resultado de la búsqueda, una matriz):

$("input").bind("autocompletesearchcomplete", function(event, contents) {
    $("#results").html(contents.length);
});

Lo que está sucediendo aquí es que está guardando la responsefunción de autocompletar en una variable ( __response) y luego la usa applypara llamarla nuevamente. No puedo imaginar ningún efecto negativo de este método ya que está llamando al método predeterminado. Dado que estamos modificando el prototipo del objeto, esto funcionará para todos los widgets de autocompletar.

Aquí hay un ejemplo de trabajo : http://jsfiddle.net/andrewwhitaker/VEhyV/

Mi ejemplo usa una matriz local como fuente de datos, pero no creo que eso deba importar.


Actualización: también puede envolver la nueva funcionalidad en su propio widget, extendiendo la funcionalidad predeterminada de autocompletar:

$.widget("ui.customautocomplete", $.extend({}, $.ui.autocomplete.prototype, {

  _response: function(contents){
      $.ui.autocomplete.prototype._response.apply(this, arguments);
      $(this.element).trigger("autocompletesearchcomplete", [contents]);
  }
}));

Cambiar su llamada de .autocomplete({...});a:

$("input").customautocomplete({..});

Y luego enlazar al autocompletesearchcompleteevento personalizado más tarde:

$("input").bind("autocompletesearchcomplete", function(event, contents) {
    $("#results").html(contents.length);
});

Vea un ejemplo aquí : http://jsfiddle.net/andrewwhitaker/VBTGJ/


Dado que esta pregunta / respuesta ha recibido algo de atención, pensé en actualizar esta respuesta con otra forma de lograrlo. Este método es más útil cuando solo tiene un widget de autocompletar en la página. Esta forma de hacerlo se puede aplicar a un widget de autocompletar que usa una fuente remota o local:

var src = [...];

$("#auto").autocomplete({
    source: function (request, response) {
        var results = $.ui.autocomplete.filter(src, request.term);

        if (!results.length) {
            $("#no-results").text("No results found!");
        } else {
            $("#no-results").empty();
        }

        response(results);
    }
});

Dentro de ifes donde colocaría su lógica personalizada para ejecutar cuando no se detectan resultados.

Ejemplo: http://jsfiddle.net/qz29K/

Si está utilizando una fuente de datos remota, diga algo como esto:

$("#auto").autocomplete({
    source: "my_remote_src"
});

Luego, deberá cambiar su código para que pueda hacer que AJAX se llame usted mismo y pueda detectar cuándo regresan 0 resultados:

$("#auto").autocomplete({
    source: function (request, response) {
        $.ajax({
            url: "my_remote_src", 
            data: request,
            success: function (data) {
                response(data);
                if (data.length === 0) {
                    // Do logic for empty result.
                }
            },
            error: function () {
                response([]);
            }
        });
    }
});
Andrew Whitaker
fuente
@Andrew, ¿alguna idea de cómo puedo acceder a los elementos en la matriz de "contenido" usando jQuery?
Bongs
1
@Bongs: Debería poder acceder a él directamente por índicecontents[0]
Andrew Whitaker
En realidad, el problema es que la matriz de contenido se completó con el nombre de usuario y su imagen, y no pudo acceder a ella especificando el valor del índice. Pero descubrí la solución. Tuve que mencionar como, contenido [i] .user.username ... :) gracias por la respuesta y la increíble solución ...
Bongs
La solución anterior también funciona muy bien para el autocompletado de PrimeFaces (2.2.x) que se basa en el mismo complemento de jQuery.
wrschneider
3
En JqueryUI 1.8.19, la función _response fue renombrada a __response. ( goo.gl/zAl88 ). Entonces, $ .ui.autocomplete.prototype._response se convierte en $ .ui.autocomplete.prototype .__ response
crazyphoton
6

Todo el mundo parece estar ignorando la forma fácil e incorporada: use el evento messages: noResults.

$('#field_name').autocomplete({
  source: $('#field_name').data('autocomplete-source'),
  messages: {
    noResults: function(count) {
      console.log("There were no matches.")
    },
    results: function(count) {
      console.log("There were " + count + " matches")
    }
  }
})

Esta característica se agregó en jQuery 1.9, como una característica experimental ( descrita aquí ). En julio de 2017, aún no está documentado en la API .

Mike Bethany
fuente
2

Si está utilizando una fuente de datos remota (como una base de datos MySQL, PHP o lo que sea en el lado del servidor), hay un par de otras formas más limpias de manejar una situación en la que no hay datos para devolver al cliente (sin la necesidad de hacks o cambios en el código de la interfaz de usuario del código central).

Utilizo PHP y MySQL como mi fuente de datos remota y JSON para pasar información entre ellos. En mi caso, parecía recibir errores de excepción de jQuery si la solicitud JSON no obtenía algún tipo de respuesta del servidor, por lo que me resultó más fácil devolver una respuesta JSON vacía desde el lado del servidor cuando no hay datos y luego manejar el cliente respuesta desde allí:

if (preg_match("/^[a-zA-Z0-9_]*$/", $_GET['callback'])) {//sanitize callback name
    $callback = $_GET['callback'];
} else { die(); }

die($callback . "([])");

Otra forma sería devolver una bandera en la respuesta del servidor para indicar que no hay datos coincidentes y realizar acciones en el lado del cliente en función de la presencia (o el valor) de la bandera en la respuesta. En este caso, la respuesta del servidor sería algo como:

die($callback . "([{'nodata':true}])");

Luego, en base a esta bandera, se pueden realizar acciones en el lado del cliente:

$.getJSON('response.php?callback=?', request, function (response) {
    if (typeof response[0].nodata !== 'undefined' && response[0].nodata === true) {
        alert('No data to display!');
    } else {
        //Do whatever needs to be done in the event that there is actually data to display.
    }
});
Zappa
fuente
2

Después de inicializar su elemento de autocompletar, configure la opción de mensajes si desea usar los intervalos predeterminados para la indicación de mensajes:

$(<yourselector>).autocomplete('option', 'messages', {
    noResults: 'myKewlMessage',
    results: function( amount ) {
        return amount + ( amount > 1 ? " results were" : " result was" ) + " found.";
    }
});

NOTA : Esta es una API experimental (no documentada). Los desarrolladores de jQuery UI todavía están investigando una solución completa para la manipulación e internacionalización de cadenas.

Guntram
fuente
0

Después de horas jugando, finalmente encontré un truco para mostrar No match founden el autocompletado de jQuery. Mire el código anterior y simplemente agregue a div, en mi caso #ulNoMatchy su estilo establecido en displap:none. En el método de devolución de llamada exitosa, verifique si la matriz devuelta tiene length == 0. Si está ahí lo tienes, ¡te alegraste el día! :)

<pre><div class="ui-widget1" style="width: auto;">
    <asp:TextBox ID="txtSearch" class="tb" runat="server" Width="150px">
    </asp:TextBox>
    <ul id="ulNoMatch" class="ui-autocomplete ui-menu ui-widget1 ui-widget1-content ui-corner-all"
        role="listbox" aria-activedescendant="ui-active-menuitem" style="z-index: 16;
        display: none; width: 150px;">
        <li class="ui-menu-item" role="menuitem"><a class="ui-corner-all" tabindex="-1">No Matches
            Found</a></li>
    </ul>
    </div><pre>
<b>
<b>

Enter code here

<script>
    $(function () {
        $("input[id$='txtSearch']").autocomplete({
            source: function (request, response) {
                $.ajax({
                    url: "splah.aspx/GetByName",
                    data: "{ 'strName': '" + request.term.trim() + "' }",
                    dataType: "json",
                    type: "POST",
                    //cacheLength: 1,
                    contentType: "application/json; charset=utf-8",
                    dataFilter: function (data) {
                        return data; },
                    success: function (data) {
                        var found = $.map(data.d, function (item) {
                            return {
                                value: item.Name,
                                id: item.id
                            }
                         });

                         if (found.length == 0)
                         {
                             $("#ulNoMatch").show();
                         }
                         else
                         {
                             $("#ulNoMatch").hide();
                         }
                         response(found);
                    },
                    error: function (XMLHttpRequest, textStatus, errorThrown) {
                        alert(textStatus);
                    }
                });
            },
            select: function (event, ui) {
                $("input[id$='txtSearch']").val(ui.item.label);
                $("input[id$='txtID']").val(ui.item.id);
                return false;
            },
            minLength: 1
        });
    });
</script>
Umar Malik
fuente
0

No veo por qué el sourceparámetro con una devolución de llamada personalizada no es suficiente:

$("#autocomplete").autocomplete({
    source: function (request, response) {
        $.ajax({
            url: "http://example.com/service.json",
            data: {
                q: this.term
            },
            success: function (data, textStatus, jqXHR) {
                // data would be an array containing 0 or more items
                console.log("[SUCCESS] search returned " + data.length + " item(s)");
                response(data);
            },
            error: function (jqXHR, textStatus, errorThrown) {
                // triggered when AJAX failed because of, for example, malformed JSON
                console.log("[FAILURE] search returned error");
                response([]);
            }
        });
    }
});
Salman A
fuente
-1
function SearchText() {
 $(".autosuggest").autocomplete({
   source: function (request, response) {
    $.ajax({
     type: "POST",
     contentType: "application/json; charset=utf-8",
      url: "Default.aspx/GetAutoCompleteData",
      data: "{'username':'" + document.getElementById('txtSearch').value + "'}",
        dataType: "json",
        success: function (data.d) {
        if ((data.d).length == 0) {
         alert("no result found");
          }
           response(data.d);
         },
         error: function (result) {
              alert("Error");
         }
         });
        }
     });
  }
selvin john
fuente
Esta respuesta no aporta nada nuevo, la respuesta aceptada tiene el mismo código.
Martin
-1
The easiest straight forward way to do it.

$("#search-box").autocomplete({
                    minLength: 2,
                    source:function (request, response) {
                        $.ajax({
                            url: urlPref + "/Api/SearchItems",
                            data: {
                                term: request.term
                            },
                            success: function (data) {
                                if (data.length == 0) {
                                    data.push({
                                        Id: 0,
                                        Title: "No results found"
                                    });
                                }
                                response(data);
                            }
                            });
                        },
Bishoy Hanna
fuente
Esta respuesta no aporta nada nuevo, la respuesta aceptada tiene el mismo código.
Martin