Não detectando resultados no preenchimento automático da IU do jQuery

89

Antes de você me apontar para eles, sim, eu revi meia dúzia de postagens sobre este tópico, mas ainda não consigo entender por que isso não funciona.

Meu objetivo é detectar quando o preenchimento automático produz 0 resultados. Aqui está o 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!'); }

  })
 }
}); 

A pesquisa em si funciona bem, posso fazer com que os resultados apareçam sem problemas. Pelo que entendi, devo ser capaz de interceptar os resultados com o manipulador de preenchimento automático ("resultado"). Nesse caso, ele nunca dispara. (Mesmo um alerta genérico ou console.log que não faz referência ao número de resultados nunca dispara). O manipulador de eventos open mostra o número correto de resultados (quando há resultados), e os manipuladores de eventos search e close relatam um tamanho de resultado que está sempre um passo atrás.

Eu sinto que estou perdendo algo óbvio e gritante aqui, mas eu simplesmente não vejo isso.

ScottyDont
fonte
Parece que não há uma maneira fácil de fazer isso com um widget de preenchimento automático conduzido por dados do lado do cliente. O uso de uma fonte remota para o widget é uma opção?
Andrew Whitaker

Respostas:

199

jQueryUI 1.9

jQueryUI 1.9 abençoou o widget de preenchimento automático com o response evento, que podemos aproveitar para detectar se nenhum resultado foi retornado:

Disparado após a conclusão de uma pesquisa, antes de o menu ser mostrado. Útil para manipulação local de dados de sugestão, onde um retorno de chamada de opção de fonte personalizada não é necessário. Este evento é sempre acionado quando uma pesquisa é concluída, mesmo que o menu não seja mostrado porque não há resultados ou o preenchimento automático está desabilitado.

Então, com isso em mente, o hacking que tivemos que fazer no jQueryUI 1.8 foi substituído por:

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

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


jQueryUI 1.8

Não consegui encontrar uma maneira direta de fazer isso com a API jQueryUI, no entanto, você pode substituir a autocomplete._responsefunção pela sua própria e, em seguida, chamar a função jQueryUI padrão ( atualizada para estender o prototypeobjeto do preenchimento automático ) :

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

E, em seguida, vincule um manipulador de autocompletesearchcompleteeventos ao evento (o conteúdo é o resultado da pesquisa, uma matriz):

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

O que está acontecendo aqui é que você está salvando a responsefunção de preenchimento automático em uma variável ( __response) e usando applypara chamá-la novamente. Não consigo imaginar nenhum efeito negativo desse método, já que você está chamando o método padrão. Como estamos modificando o protótipo do objeto, isso funcionará para todos os widgets de preenchimento automático.

Aqui está um exemplo prático : prático http://jsfiddle.net/andrewwhitaker/VEhyV/

Meu exemplo usa uma matriz local como fonte de dados, mas não acho que isso deva importar.


Atualização: você também pode envolver a nova funcionalidade em seu próprio widget, estendendo a funcionalidade de preenchimento automático padrão:

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

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

Alterando sua chamada de .autocomplete({...});para:

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

E depois vincule ao autocompletesearchcompleteevento personalizado :

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

Veja um exemplo aqui : http://jsfiddle.net/andrewwhitaker/VBTGJ/


Como essa pergunta / resposta recebeu alguma atenção, pensei em atualizar esta resposta com outra maneira de fazer isso. Este método é mais útil quando você tem apenas um widget de preenchimento automático na página. Essa maneira de fazer isso pode ser aplicada a um widget de preenchimento automático que usa uma fonte remota ou 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 do ifé onde você colocaria sua lógica customizada para executar quando nenhum resultado fosse detectado.

Exemplo: http://jsfiddle.net/qz29K/

Se você estiver usando uma fonte de dados remota, diga algo assim:

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

Em seguida, você precisará alterar seu código para que você mesmo faça a chamada AJAX e possa detectar quando nenhum resultado voltar:

$("#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
fonte
@Andrew, alguma ideia de como posso acessar os elementos do array "contents" usando jQuery ???
Bongs
1
@Bongs: Você deve ser capaz de acessá-lo diretamente pelo índicecontents[0]
Andrew Whitaker
Na verdade, a matriz de conteúdo foi preenchida com nome de usuário e sua imagem e não foi capaz de acessá-la especificando o valor do índice. Mas descobri a solução. Tive que mencionar like, contents [i] .user.username ... :) obrigado pela resposta e pela solução incrível ...
Bongs
A solução acima também funciona muito bem para autocomplete PrimeFaces (2.2.x) que é baseado no mesmo plugin jQuery.
wrschneider
3
No JqueryUI 1.8.19, a função _response foi renomeada para __response. ( goo.gl/zAl88 ). Então, $ .ui.autocomplete.prototype._response se torna $ .ui.autocomplete.prototype .__ resposta
crazyphoton
6

Todos parecem estar ignorando a maneira fácil e embutida: use o 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")
    }
  }
})

Este recurso foi adicionado no jQuery 1.9, como um recurso experimental ( descrito aqui ). Em julho de 2017, ainda não estava documentado na API .

Mike Bethany
fonte
2

Se você estiver usando uma fonte de dados remota (como um banco de dados MySQL, PHP ou qualquer outro no lado do servidor), existem algumas outras maneiras mais limpas de lidar com uma situação em que não há dados para retornar ao cliente (sem a necessidade de nenhum hacks ou alterações de código de IU do código principal).

Eu uso PHP e MySQL como minha fonte de dados remota e JSON para passar informações entre eles. No meu caso, parecia que recebia erros de exceção do jQuery se a solicitação JSON não obtivesse algum tipo de resposta do servidor, então achei mais fácil apenas retornar uma resposta JSON vazia do lado do servidor quando não há dados e, em seguida, lidar com o cliente resposta de lá:

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

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

Outra forma seria retornar um sinalizador na resposta do servidor para indicar que não há dados correspondentes e realizar ações no lado do cliente com base na presença (e / ou valor) do sinalizador na resposta. Nesse caso, a resposta do servidor seria algo como:

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

Então, com base neste sinalizador, as ações podem ser realizadas no lado do 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
fonte
2

Depois de inicializar o elemento autocomplete, defina a opção de mensagens se quiser usar os spans padrão para indicação de mensagem:

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

NOTA : Esta é uma API experimental (não documentada). Os desenvolvedores do jQuery UI ainda estão investigando uma solução completa para manipulação de strings e internacionalização.

Guntram
fonte
0

Depois de horas jogando, finalmente encontrei um truque para exibir No match foundno autocomplete jQuery. Observe o código acima e simplesmente adicione a div, no meu caso #ulNoMatche seu estilo definido como displap:none. No método de sucesso de retorno de chamada, verifique se o array retornado tem length == 0. Se for lá, você fez o seu dia! :)

<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
fonte
0

Não vejo por que o sourceparâmetro com um retorno de chamada personalizado não é 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
fonte
-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");
         }
         });
        }
     });
  }
john selvin
fonte
Esta resposta não contribui com nada de novo, a resposta aceita tem o mesmo 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
fonte
Esta resposta não contribui com nada de novo, a resposta aceita tem o mesmo código.
Martin