web-dev-qa-db-fra.com

Twitter Bootstrap Typeahead - Id & Label

J'utilise Bootstrap 2.1.1 et jQuery 1.8.1 et j'essaie d'utiliser les fonctionnalités de Typeahead.

J'essaie d'afficher un label et j'utilise un id comme un standard <select />

Voici mon initialisation typeahead:

$(':input.autocomplete').typeahead({
    source: function (query, process) {
        $('#autocompleteForm .query').val(query);
        return $.get(
            $('#autocompleteForm').attr('action')
          , $('#autocompleteForm').serialize()
          , function (data) {
              return process(data);
          }
        );
    }
});

Voici le genre de JSON que j'envoie

[{"id":1,"label":"machin"},{"id":2,"label":"truc"}]

Comment puis-je dire à process() d'afficher mes étiquettes et de stocker l'ID sélectionné dans un autre champ masqué?

44
Pierre de LESPINAY

Il y a un excellent tutoriel ici qui explique comment faire cela: http://tatiyants.com/how-to-use-json-objects-with-Twitter-bootstrap-typeahead/ (lire mon commentaire sur cette page si elle n'a pas encore été reflétée dans la partie principale du message).

Sur la base de ce didacticiel et du JSON que vous avez fourni, vous pouvez faire quelque chose comme ceci:

$(':input.autocomplete').typeahead({
    source: function(query, process) {
        objects = [];
        map = {};
        var data = [{"id":1,"label":"machin"},{"id":2,"label":"truc"}] // Or get your JSON dynamically and load it into this variable
        $.each(data, function(i, object) {
            map[object.label] = object;
            objects.Push(object.label);
        });
        process(objects);
    },
    updater: function(item) {
        $('hiddenInputElement').val(map[item].id);
        return item;
    }
});                    
79
Gerbus

Depuis la version 0.10.1 de Twitter Typeahead ( https://github.com/Twitter/typeahead.js ), Id/Label est pris en charge de manière native:

  $('input[name=address]').typeahead({
        hint: false
    }, {
        source: function (query, cb) {
            $.ajax({
                url: '/api/addresses?q=' + encodeURIComponent(query),
                dataType: 'json',
                cache: false,
                type: 'GET',
                success: function (response, textStatus, jqXHR) {
                    cb(response.data);
                },
                error: function (jqXHR, textStatus, errorThrown) {
                }
            });
        },
        name: 'addresses',
        displayKey: 'text'
    }).on('typeahead:selected', function (e, suggestion, name) {
        window.location.href = '/' + suggestion.id;
    });

Si l'exemple ci-dessus, je passe un tableau d'objets au rappel source (cb). En spécifiant displayKey: 'text', je dis à la bibliothèque d'utiliser la propriété 'text' pour la suggestion automatique. Lorsque le rappel 'typeahead: select' est appelé, le deuxième argument passé (suggestion) contient l'objet qui a été sélectionné.

13
Johnny Oshika

Pour clarifier ce que je disais dans mon commentaire. Si vous vouliez plusieurs types d'avance sur la même page, vous devez définir chacun d'eux dans une fonction et créer une variable de carte distincte pour eux.

function initFromField() {
    var map;
    $('#from:input.autocomplete').typeahead({
        source: function(query, process) {
            map = {};
            var data = [{"id":1,"label":"machin"},{"id":2,"label":"truc"}] // Or get your JSON dynamically and load it into this variable
            objects = constructMap(data, map);
            process(objects);
        },
        updater: function(item) {
            $('#hidden-from-input').val(map[item].id);
            return item;
        }
    });
}

function initToField() {
    var map;
    $('#to:input.autocomplete').typeahead({
        source: function(query, process) {
            objects = [];
            map = {};
            var data = [{"id":1,"label":"machin"},{"id":2,"label":"truc"}] // Or get your JSON dynamically and load it into this variable
            objects = constructMap(data, map);
            process(objects);
        },
        updater: function(item) {
            $('#hidden-to-input').val(map[item].id);
            return item;
        }
    });
}

function constructMap(data, map) {
    var objects = [];
    $.each(data, function(i, object) {
        map[object.label] = object;
        objects.Push(object.label);
    });
    return objects;
}

$(function initFields() {
    initFromField();
    initToField();
});

Notez comment j'ai défini la variable de carte dans les deux fonctions d'initialisation de champ. Ceci est important, il garantit que la même variable de carte n'est pas utilisée par les deux champs de saisie.

6
Michael Yagudaev

Le problème que j'ai vu avec certaines de ces solutions est que la fonction source est appelée de façon répétée à chaque événement keyup de la zone de saisie. Cela signifie que les tableaux sont en cours de création et de boucle sur chaque événement de keyup.

Ce n'est pas nécessaire. À l'aide d'une fermeture, vous ne pouvez traiter les données qu'une seule fois et en conserver une référence à partir de la fonction source. De plus, la solution suivante résout le problème d'espace de noms global de la solution de @ Gerbus et vous permet également de jouer avec le tableau de données une fois que l'utilisateur a sélectionné quelque chose (par exemple, supprimer cet élément de la liste).

  // Setup the auto-complete box of users
  var setupUserAcUi = function(data) {
      var objects = [];
      var map = {};
      $.each(data, function(i, object) {
          map[object.name] = object;
          objects.Push(object.name);
      });

      // The declaration of the source and updater functions, and the fact they
      // are referencing variables outside their scope, creates a closure
      $("#splitter-findusers").typeahead({
        source: function(query, process) {
            process(objects);
        },
        updater: function(item) {
            var mapItem = map[item];
            objects.splice( $.inArray(item, objects), 1 ); // Remove from list
            // Perform any other actions
        }
      });
  };

  // `data` can be an array that you define,
  // or you could pass `setupUserAcUi` as the callback to a jQuery.ajax() call
  // (which is actually how I am using it) which returns an array
  setupUserAcUi(data);
3
vcardillo

J'ai moi-même eu des problèmes avec ce problème, voici la solution que j'ai trouvée pour les données du type:

[{'id':an_id, 'name':a_name}]

Était:

$("#memberSearch").typeahead({
            source: function (query, process) {
                var $this = this //get a reference to the typeahead object
                return $.get('/getSwimmerListJSON',function(data){
                    var options = [];
                    $this["map"] = {}; //replace any existing map attr with an empty object
                    $.each(data,function (i,val){
                        options.Push(val.name);
                        $this.map[val.name] = val.id; //keep reference from name -> id
                    });
                    return process(options);
                });
            },
            updater: function (item) {
                console.log(this.map[item],item); //access it here

            }

        });
3
HennyH

Voici une solution encapsulée. Cette solution vous permet d'avoir plus d'une frappe sur la même page.

Ceci est une version modifiée de # 13279176 réponse Gerbus.

$('.make-me-typeahead').typeahead({
    source: function (query) {
        var self = this;
        self.map = {};
        var items = [];

        var data = [
            {"id": 1, "label": "machin"},
            {"id": 2, "label": "truc"}
        ];

        $.each(data, function (i, item) {
            self.map[item.label] = item;
            items.Push(item.label)
        });

        return items;
    },

    updater: function (item) {
        var selectedItem = this.map[item];
        this.$element.data('selected', selectedItem);
        return item;
    }
});

Maintenant, lorsque vous avez besoin d'obtenir la clé de l'élément sélectionné, il vous suffit de faire $('.make-me-typeahead').data('selected')

3
Capy

La réponse choisie est un peu un hack. Je cherchais la même chose, et cette approche fonctionne à merveille:

https://github.com/twbs/bootstrap/pull/3682

Il conserve deux tableaux, un pour le nom affiché par typeahead et un pour l'objet dont le nom est extrait. Lorsqu'une des options est sélectionnée, elle utilise le nom pour trouver l'objet d'où il vient.

3
Kevin Lawrence

Juste une autre façon d'implémenter la fonction Pierref.

var separator = "####";
$("'.autocomplete'").typeahead({
    minLength: 3,
    source: function (query, process) {
        var config = {
            type: 'POST',
            url: 'Requests/AJAX.PHP', //Change it
            cache: 'false',
            data: {
                query: query
            },
            dataType: 'json'
        };

        config.beforeSend = function () {
            //TODO : loading gif
        };

        config.error = function (json) {
            if (json.error) {
                alert(json.error);
            }
        };

        config.success = function (json) {
            if (json.error) {
                alert(json.error);
            }
            var data = [];
            for (var i = 0; i < json.data.length; i++) {
                data.Push(json.data[i].id + separator + json.data[i].name);
            }

            process(data);
        };

        $.ajax(config);
    },
    highlighter: function (item) {
        var parts = item.split(separator);
        parts.shift();
        return parts.join(separator);
    },
    updater: function (item) {
        var parts = item.split(separator);
        $('.autocomplete').val(parts.shift());
        return parts.join(separador);
    }
});
1
Pedro Muniz

La réponse sélectionnée ne traite pas d'étiquettes non uniques (par exemple, le nom d'une personne). J'utilise ce qui suit qui conserve le formatage du surligneur par défaut:

            var callback = function(id) {
                console.log(id);
            };

            $('.typeahead',this.el).typeahead({
                source: function (query, process) {

                    var sourceData = [
                        {id:"abc",label:"Option 1"},
                        {id:"hfv",label:"Option 2"},
                        {id:"jkf",label:"Option 3"},
                        {id:"ds",label:"Option 4"},
                        {id:"dsfd",label:"Option 5"},
                    ];

                    var concatSourceData = _.map(sourceData,function(item){
                        return item.id + "|" + item.label;
                    });

                    process(concatSourceData);
                },

                matcher : function(item) {
                    return this.__proto__.matcher.call(this,item.split("|")[1]);
                },

                highlighter: function(item) {
                    return this.__proto__.highlighter.call(this,item.split("|")[1]);
                },

                updater: function(item) {
                    var itemArray = item.split("|");
                    callback(itemArray[0]);
                    return this.__proto__.updater.call(this,itemArray[1]);
                }
            });
1
will.ogden