Wykrywanie braku wyników w autouzupełnianiu interfejsu użytkownika jQuery

89

Zanim mi do nich wskażesz, tak, przejrzałem pół tuzina postów na ten temat, ale nadal nie wiem, dlaczego to nie działa.

Moim celem jest wykrycie, kiedy autouzupełnianie daje 0 wyników. Oto kod:

 $.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!'); }

  })
 }
}); 

Samo wyszukiwanie działa dobrze, wyniki pojawiają się bez problemu. Jak rozumiem, powinienem być w stanie przechwycić wyniki za pomocą modułu obsługi autouzupełniania („wyników”). W tym przypadku w ogóle nie strzela. (Nawet ogólny alert lub console.log, który nie odwołuje się do liczby wyników, nigdy nie jest uruchamiany). Procedura obsługi zdarzenia otwierania pokazuje poprawną liczbę wyników (jeśli istnieją wyniki), a programy obsługi zdarzeń wyszukiwania i zamykania raportują rozmiar wyniku, który jest zawsze o jeden krok za.

Czuję, że brakuje mi czegoś oczywistego i rażącego, ale po prostu tego nie widzę.

ScottyDont
źródło
Wygląda na to, że nie ma łatwego sposobu na osiągnięcie tego za pomocą widżetu autouzupełniania opartego na danych po stronie klienta. Czy użycie zdalnego źródła dla widgetu jest opcją?
Andrew Whitaker,

Odpowiedzi:

200

jQueryUI 1.9.0

jQueryUI 1.9 pobłogosławił widżet autouzupełniania responsezdarzeniem, które możemy wykorzystać do wykrycia, jeśli żadne wyniki nie zostały zwrócone:

Wywoływane po zakończeniu wyszukiwania, przed wyświetleniem menu. Przydatne do lokalnego manipulowania danymi sugestii, gdy nie jest wymagane wywołanie zwrotne opcji niestandardowego źródła. To zdarzenie jest wywoływane zawsze po zakończeniu wyszukiwania, nawet jeśli menu nie zostanie wyświetlone, ponieważ nie ma wyników lub funkcja autouzupełniania jest wyłączona.

Mając to na uwadze, hakowanie, które musieliśmy zrobić w jQueryUI 1.8, zostało zastąpione przez:

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

Przykład: http://jsfiddle.net/andrewwhitaker/x5q6Q/


jQueryUI 1.8.0

Nie mogłem znaleźć prostego sposobu na zrobienie tego za pomocą interfejsu API jQueryUI, jednak możesz zastąpić autocomplete._responsefunkcję własną, a następnie wywołać domyślną funkcję jQueryUI ( zaktualizowaną w celu rozszerzenia prototypeobiektu autouzupełniania ) :

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

Następnie powiąż procedurę obsługi zdarzeń ze autocompletesearchcompletezdarzeniem (zawartość jest wynikiem wyszukiwania, tablicą):

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

Chodzi tutaj o to, że zapisujesz responsefunkcję autouzupełniania w zmiennej ( __response), a następnie używasz jej applydo ponownego wywołania. Nie mogę sobie wyobrazić żadnych złych skutków tej metody, ponieważ wywołujesz metodę domyślną. Ponieważ modyfikujemy prototyp obiektu, będzie to działać dla wszystkich widżetów autouzupełniania.

Oto działający przykład : http://jsfiddle.net/andrewwhitaker/VEhyV/

Mój przykład używa tablicy lokalnej jako źródła danych, ale nie sądzę, żeby to miało znaczenie.


Aktualizacja: Możesz również opakować nową funkcjonalność we własnym widżecie, rozszerzając domyślną funkcję autouzupełniania:

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

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

Zmiana połączenia z .autocomplete({...});na:

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

A później powiąż ze autocompletesearchcompletezdarzeniem niestandardowym :

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

Zobacz przykład tutaj : http://jsfiddle.net/andrewwhitaker/VBTGJ/


Ponieważ to pytanie / odpowiedź zwróciło uwagę, pomyślałem, że zaktualizuję tę odpowiedź o jeszcze inny sposób, aby to osiągnąć. Ta metoda jest najbardziej przydatna, gdy na stronie znajduje się tylko jeden widżet autouzupełniania. Ten sposób można to zrobić w przypadku widgetu autouzupełniania, który korzysta ze źródła zdalnego lub lokalnego:

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

Wewnątrz ifjest miejsce, w którym można umieścić niestandardową logikę do wykonania, gdy nie zostaną wykryte żadne wyniki.

Przykład: http://jsfiddle.net/qz29K/

Jeśli korzystasz ze zdalnego źródła danych, powiedz coś takiego:

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

Następnie musisz zmienić kod, aby samodzielnie wykonać połączenie AJAX i wykryć, kiedy wróci 0 wyników:

$("#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
źródło
@Andrew, jakikolwiek pomysł, jak mogę uzyskać dostęp do elementów w tablicy "content" za pomocą jQuery ???
Bongi
1
@Bongi: Powinieneś mieć do niego bezpośredni dostęp przez indekscontents[0]
Andrew Whitaker,
Właściwie chodzi o to, że tablica content została wypełniona nazwą użytkownika i jego obrazem i nie była w stanie uzyskać do niej dostępu poprzez określenie wartości indeksu. Ale wymyśliłem rozwiązanie. Musiałem wspomnieć o polubieniu, zawartość [i] .user.username ... :) dzięki za odpowiedź i niesamowite rozwiązanie ...
Bongi
Powyższe rozwiązanie świetnie sprawdza się również w przypadku autouzupełniania PrimeFaces (2.2.x), które jest oparte na tej samej wtyczce jQuery.
wrschneider
3
W JqueryUI 1.8.19 nazwa funkcji _response została zmieniona na __response. ( goo.gl/zAl88 ). Tak więc $ .ui.autocomplete.prototype._response zmieni się w $ .ui.autocomplete.prototype .__ odpowiedź
crazyphoton
6

Wydaje się, że wszyscy ignorują prosty, wbudowany sposób: użyj zdarzenia 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")
    }
  }
})

Ta funkcja została dodana w jQuery 1.9 jako funkcja eksperymentalna ( opisana tutaj ). W lipcu 2017 nie jest to jeszcze udokumentowane w API .

Mike Bethany
źródło
2

Jeśli korzystasz ze zdalnego źródła danych (takiego jak baza danych MySQL, PHP lub cokolwiek po stronie serwera), istnieje kilka innych czystszych sposobów radzenia sobie z sytuacją, gdy nie ma danych do zwrócenia do klienta (bez potrzeby hacki lub podstawowe zmiany kodu interfejsu użytkownika).

Używam PHP i MySQL jako mojego zdalnego źródła danych i JSON do przekazywania informacji między nimi. W moim przypadku wydawało mi się, że otrzymuję błędy wyjątku jQuery, jeśli żądanie JSON nie otrzymało jakiejś odpowiedzi z serwera, więc łatwiej mi było po prostu zwrócić pustą odpowiedź JSON po stronie serwera, gdy nie ma danych, a następnie obsłużyć klienta odpowiedź stamtąd:

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

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

Innym sposobem byłoby zwrócenie flagi w odpowiedzi z serwera, aby wskazać, że nie ma pasujących danych i wykonać działania po stronie klienta na podstawie obecności (i lub wartości) flagi w odpowiedzi. W tym przypadku odpowiedź serwera wyglądałaby następująco:

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

Następnie na podstawie tej flagi można wykonać akcje po stronie klienta:

$.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
źródło
2

Po zainicjowaniu elementu autouzupełniania ustaw opcję wiadomości, jeśli chcesz używać domyślnych zakresów do wskazywania wiadomości:

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

UWAGA : to jest eksperymentalny interfejs API (nieudokumentowany). Programiści jQuery UI wciąż badają pełne rozwiązanie do manipulacji ciągami znaków i internacjonalizacji.

Guntram
źródło
0

Po godzinach grania w końcu znalazłem sztuczkę do wyświetlenia No match foundw autouzupełnianiu jQuery. Spójrz na powyższy kod i po prostu dodaj div, w moim przypadku #ulNoMatchi jego styl ustawiony na displap:none. W metodzie powodzenia wywołania zwrotnego sprawdź, czy zwrócona tablica ma length == 0. Jeśli tam jesteś, masz dobry dzień! :)

<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
źródło
0
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
źródło
Ta odpowiedź nie wnosi nic nowego, zaakceptowana odpowiedź ma ten sam kod.
Martin
0

Nie rozumiem, dlaczego sourceparametr z niestandardowym wywołaniem zwrotnym nie wystarczy:

$("#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
źródło
-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
źródło
Ta odpowiedź nie wnosi nic nowego, zaakceptowana odpowiedź ma ten sam kod.
Martin