Détection d'aucun résultat sur la saisie semi-automatique de l'interface utilisateur jQuery

89

Avant de me les indiquer, oui, j'ai examiné la demi-douzaine de messages sur ce sujet, mais je ne comprends toujours pas pourquoi cela ne fonctionne pas.

Mon objectif est de détecter lorsque la saisie semi-automatique donne 0 résultat. Voici le code:

 $.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 recherche elle-même fonctionne correctement, je peux faire apparaître les résultats sans problème. Si je comprends bien, je devrais être capable d'intercepter les résultats avec le gestionnaire de saisie semi-automatique ("result"). Dans ce cas, il ne se déclenche jamais du tout. (Même une alerte générique ou console.log qui ne fait pas référence au nombre de résultats ne se déclenche jamais). Le gestionnaire d'événements d'ouverture affiche le nombre correct de résultats (lorsqu'il y a des résultats), et les gestionnaires d'événements de recherche et de fermeture signalent une taille de résultat toujours en retard.

J'ai l'impression de manquer quelque chose d'évident et de flagrant ici, mais je ne le vois tout simplement pas.

ScottyDont
la source
Il semble qu'il n'y ait pas de moyen facile d'accomplir cela avec un widget de saisie semi-automatique piloté par des données côté client. L'utilisation d'une source distante pour le widget est-elle une option?
Andrew Whitaker

Réponses:

199

jQueryUI 1.9

jQueryUI 1.9 a béni le widget de saisie semi-automatique avec l' responseévénement, que nous pouvons exploiter pour détecter si aucun résultat n'a été renvoyé:

Déclenché après la fin d'une recherche, avant que le menu ne s'affiche. Utile pour la manipulation locale des données de suggestion, où un rappel d'option de source personnalisé n'est pas nécessaire. Cet événement est toujours déclenché lorsqu'une recherche se termine, même si le menu ne sera pas affiché car il n'y a aucun résultat ou la saisie semi-automatique est désactivée.

Donc, dans cet esprit, le piratage que nous avons dû faire dans jQueryUI 1.8 est remplacé par:

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

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


jQueryUI 1.8

Je n'ai pas trouvé de moyen simple de le faire avec l'API jQueryUI, cependant, vous pouvez remplacer la autocomplete._responsefonction par la vôtre, puis appeler la fonction jQueryUI par défaut ( mise à jour pour étendre l' prototypeobjet de la saisie semi-automatique ) :

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

Et puis liez un gestionnaire d'événements à l' autocompletesearchcompleteévénement (le contenu est le résultat de la recherche, un tableau):

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

Ce qui se passe ici, c'est que vous enregistrez la responsefonction de saisie semi-automatique dans une variable ( __response), puis que vous l'utilisez applypour la rappeler. Je ne peux imaginer aucun effet néfaste de cette méthode puisque vous appelez la méthode par défaut. Puisque nous modifions le prototype de l'objet, cela fonctionnera pour tous les widgets de saisie semi-automatique.

Voici un exemple de travail : http://jsfiddle.net/andrewwhitaker/VEhyV/

Mon exemple utilise un tableau local comme source de données, mais je ne pense pas que cela devrait avoir de l'importance.


Mise à jour: vous pouvez également intégrer la nouvelle fonctionnalité dans son propre widget, étendant la fonctionnalité de saisie semi-automatique par défaut:

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

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

Modification de votre appel de .autocomplete({...});à:

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

Et puis liez-vous à l' autocompletesearchcompleteévénement personnalisé plus tard:

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

Voir un exemple ici : http://jsfiddle.net/andrewwhitaker/VBTGJ/


Étant donné que cette question / réponse a attiré l'attention, j'ai pensé mettre à jour cette réponse avec une autre façon d'accomplir cela. Cette méthode est particulièrement utile lorsque vous n'avez qu'un seul widget de saisie semi-automatique sur la page. Cette façon de faire peut être appliquée à un widget de saisie semi-automatique qui utilise une source distante ou 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);
    }
});

À l'intérieur de, ifvous placeriez votre logique personnalisée à exécuter lorsqu'aucun résultat n'est détecté.

Exemple: http://jsfiddle.net/qz29K/

Si vous utilisez une source de données distante, dites quelque chose comme ceci:

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

Ensuite, vous devrez modifier votre code afin de faire l'appel AJAX vous-même et de détecter le retour de 0 résultat:

$("#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
la source
@Andrew, une idée de la façon dont je peux accéder aux éléments du tableau "contents" en utilisant jQuery ???
Bongs
1
@Bongs: Vous devriez pouvoir y accéder directement par indexcontents[0]
Andrew Whitaker
En fait, le problème est que le tableau de contenu a été rempli avec le nom d'utilisateur et son image, et n'a pas pu y accéder en spécifiant la valeur d'index. Mais j'ai trouvé la solution. Je devais mentionner comme, contents [i] .user.username ... :) merci pour la réponse et la solution géniale ...
Bongs
La solution ci-dessus fonctionne également très bien pour la saisie semi-automatique PrimeFaces (2.2.x) qui est basée sur le même plugin jQuery.
wrschneider
3
Dans JqueryUI 1.8.19, la fonction _response a été renommée __response. ( goo.gl/zAl88 ). Ainsi, $ .ui.autocomplete.prototype._response devient $ .ui.autocomplete.prototype .__ réponse
crazyphoton
6

Tout le monde semble ignorer la méthode simple et intégrée: utilisez l'événement 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")
    }
  }
})

Cette fonctionnalité a été ajoutée dans jQuery 1.9, en tant que fonctionnalité expérimentale ( décrite ici ). En juillet 2017, il n'est pas encore documenté dans l'API .

Mike Bethany
la source
2

Si vous utilisez une source de données distante (comme une base de données MySQL, PHP ou autre côté serveur), il existe plusieurs autres moyens plus propres de gérer une situation où il n'y a pas de données à renvoyer au client (sans avoir besoin de hacks ou modifications du code de l'interface utilisateur du code principal).

J'utilise PHP et MySQL comme source de données distante et JSON pour transmettre des informations entre eux. Dans mon cas, j'ai semblé obtenir des erreurs d'exception jQuery si la demande JSON n'obtenait pas une sorte de réponse du serveur, donc j'ai trouvé plus facile de simplement renvoyer une réponse JSON vide du côté serveur lorsqu'il n'y a pas de données, puis de gérer le client réponse à partir de là:

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

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

Une autre façon serait de renvoyer un indicateur dans la réponse du serveur pour indiquer qu'il n'y a pas de données correspondantes et d'effectuer des actions côté client en fonction de la présence (et / ou de la valeur) de l'indicateur dans la réponse. Dans ce cas, la réponse des serveurs serait quelque chose comme:

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

Ensuite, sur la base de cet indicateur, des actions peuvent être effectuées côté 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.
    }
});
Zappa
la source
2

Après avoir initialisé votre élément de saisie semi-automatique, définissez l'option messages si vous souhaitez utiliser les délais par défaut pour l'indication de message:

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

REMARQUE : il s'agit d'une API expérimentale (non documentée). Les développeurs de jQuery UI étudient toujours une solution complète pour la manipulation de chaînes et l'internationalisation.

Guntram
la source
0

Après des heures à jouer, j'ai finalement trouvé une astuce à afficher No match founddans la saisie semi-automatique de jQuery. Regardez le code ci-dessus et ajoutez simplement un div, dans mon cas #ulNoMatchet son style défini sur displap:none. Dans la méthode de réussite du rappel, vérifiez si le tableau renvoyé a length == 0. Si c'est là vous y allez, vous avez fait votre journée! :)

<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
la source
0

Je ne vois pas pourquoi le sourceparamètre avec un rappel personnalisé ne suffit pas :

$("#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
la source
-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
la source
Cette réponse n'apporte rien de nouveau, la réponse acceptée a le même code.
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
la source
Cette réponse n'apporte rien de nouveau, la réponse acceptée a le même code.
Martin