Rilevamento senza risultati sul completamento automatico dell’interfaccia utente jQuery

Prima di indirizzarmi verso di loro, sì, ho esaminato la mezza dozzina di post su questo argomento, ma sono ancora in imbarazzo sul motivo per cui questo non funziona.

Il mio objective è quello di rilevare quando il completamento automatico produce 0 risultati. Ecco il codice:

$.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 ricerca funziona perfettamente, posso ottenere risultati senza problemi. A quanto ho capito, dovrei essere in grado di intercettare i risultati con il gestore di completamento automatico (“risultato”). In questo caso, non si accende mai. (Anche un avviso generico o console.log che non fa riferimento al numero di risultati non viene mai triggersto). Il gestore di eventi aperto mostra il numero corretto di risultati (quando ci sono risultati), ei gestori di eventi di ricerca e chiusura riportano una dimensione dei risultati che è sempre un passo indietro.

Mi sento come se mi mancasse qualcosa di ovvio e abbagliante qui, ma semplicemente non lo vedo.

jQueryUI 1.9

jQueryUI 1.9 ha benedetto il widget di completamento automatico con l’evento di response , che possiamo sfruttare per rilevare se non sono stati restituiti risultati:

Attivato al termine di una ricerca, prima che venga visualizzato il menu. Utile per la manipolazione locale dei dati dei suggerimenti, in cui non è richiesta una richiamata opzione origine personalizzata. Questo evento viene sempre triggersto al termine di una ricerca, anche se il menu non viene visualizzato perché non ci sono risultati o il Completamento automatico è disabilitato.

Quindi, tenendo presente questo, l’hacking che dovevamo fare in jQueryUI 1.8 è stato sostituito 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(); } } }); });​ 

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


jQueryUI 1.8

Non sono riuscito a trovare un modo semplice per farlo con l’API jQueryUI, tuttavia, è ansible sostituire la funzione autocomplete._response con la propria e quindi chiamare la funzione jQueryUI predefinita ( aggiornata per estendere l’object prototype del completamento automatico) :

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

E quindi associare un gestore di eventi all’evento autocompletesearchcomplete (i contenuti sono il risultato della ricerca, una matrice):

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

Quello che sta succedendo qui è che stai salvando la funzione di response automatica su una variabile ( __response ) e poi usando apply per richiamarla di nuovo. Non riesco a immaginare alcun effetto negativo di questo metodo poiché stai chiamando il metodo predefinito. Dal momento che stiamo modificando il prototipo dell’object, questo funzionerà con tutti i widget di completamento automatico.

Ecco un esempio funzionante : http://jsfiddle.net/andrewwhitaker/VEhyV/

Il mio esempio utilizza un array locale come origine dati, ma non penso che dovrebbe essere importante.


Aggiornamento: puoi anche racchiudere la nuova funzionalità nel proprio widget, estendendo la funzionalità di completamento automatico predefinita:

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

Modifica della chiamata da .autocomplete({...}); a:

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

Quindi, esegui il collegamento con l’evento di autocompletesearchcomplete della ricerca autocompletesearchcomplete secondo momento:

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

Guarda un esempio qui : http://jsfiddle.net/andrewwhitaker/VBTGJ/


Dal momento che questa domanda / risposta ha ottenuto una certa attenzione, ho pensato di aggiornare questa risposta con un altro modo per farlo. Questo metodo è molto utile quando nella pagina è presente un solo widget di completamento automatico. Questo modo di farlo può essere applicato a un widget di completamento automatico che utilizza una fonte remota o locale:

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

All’interno di if si posiziona la logica personalizzata da eseguire quando non vengono rilevati risultati.

Esempio: http://jsfiddle.net/qz29K/

Se si sta utilizzando un’origine dati remota, dire qualcosa del genere:

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

Quindi dovrai modificare il tuo codice in modo che tu possa effettuare da solo la chiamata AJAX e in grado di rilevare quando tornano i risultati 0:

 $("#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([]); } }); } }); 

Se si utilizza un’origine dati remota (come un database MySQL, PHP o qualsiasi altra cosa sul lato server) ci sono un paio di altri modi più semplici per gestire una situazione quando non ci sono dati da restituire al client (senza la necessità di modifiche al codice UI di codice hacker o core).

Uso PHP e MySQL come sorgente dati remota e JSON per passare le informazioni tra di loro. Nel mio caso mi è sembrato di ottenere errori di eccezione jQuery se la richiesta JSON non ha ricevuto una risposta dal server, quindi ho trovato più semplice restituire una risposta JSON vuota dal lato server quando non ci sono dati e quindi gestire il client risposta da lì:

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

Un altro modo sarebbe quello di restituire un flag nella risposta dal server per indicare che non ci sono dati corrispondenti ed eseguire azioni lato client in base alla presenza (e / o valore) del flag nella risposta. In questo caso la risposta del server sarebbe qualcosa del tipo:

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

Quindi in base a questo flag le azioni possono essere eseguite lato client:

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

Tutti sembrano ignorare il modo semplice e integrato: utilizzare i messaggi: evento 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") } } }) 

Questa funzione è stata aggiunta in jQuery 1.9, come funzionalità sperimentale ( descritta qui ). A luglio 2017, non è ancora documentato nell’API .

Dopo aver inizializzato l’elemento di completamento automatico, imposta l’opzione per i messaggi se vuoi utilizzare gli span predefiniti per l’indicazione del messaggio:

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

NOTA : questa è un’API sperimentale (non documentata). Gli sviluppatori di jQuery UI stanno ancora studiando una soluzione completa per la manipolazione delle stringhe e l’internazionalizzazione.

Dopo ore di gioco ho finalmente trovato un trucco per visualizzare No match found nel completamento automatico di jQuery. Guarda il codice sopra riportato e aggiungi semplicemente un div , nel mio caso #ulNoMatch e il suo stile impostato su displap:none . Nel metodo di successo della callback, verificare se la matrice restituita ha length == 0 . Se è lì, tu hai fatto la tua giornata! 🙂

 
   Enter code here 

Non vedo perché source parametro source con un callback personalizzato non sia abbastanza. Supponendo che stiamo usando un servizio JSON (P), tieni semplicemente presente quanto segue:

Lo script lato server deve produrre JSON valido anche se non vengono trovati risultati e [] è JSON valido.

La documentazione per source parametro source suggerisce che:

Una callback di risposta, che si aspetta un singolo argomento: i dati da suggerire all’utente. Questi dati devono essere filtrati in base al termine fornito e possono essere in uno dei formati descritti sopra per dati locali semplici. È importante quando si fornisce una richiamata di origine personalizzata per gestire gli errori durante la richiesta. È necessario chiamare sempre la richiamata della risposta anche se si verifica un errore . Ciò garantisce che il widget abbia sempre lo stato corretto.

Tenendo presente i due punti precedenti, dovrebbe essere sufficiente quanto segue:

 $("#autocomplete").autocomplete({ source: function (request, response) { $.ajax({ url: "http://example.com/service.json", data: { q: this.term }, success: function (data, textStatus, jqXHR) { // data must be an array containing 0 or more items console.log("[SUCCESS] " + data.length + " item(s)"); response(data); }, error: function (jqXHR, textStatus, errorThrown) { // triggered when AJAX failed because of, for example, malformsd JSON console.log("[ERROR] n/a item(s)"); response([]); } }); } }); 
 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"); } }); } }); } 
 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); } }); },