Twitter Bootstrap Typeahead-Id & Label

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

j'essaie d'afficher un étiquette et d'utiliser un id comme une norme <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 dire process() pour afficher mes étiquettes et stocker L'ID sélectionné dans un autre champ caché?

43
demandé sur Mark Amery 2012-09-12 18:01:09

10 réponses

il y a un grand tutoriel ici qui explique comment faire ceci: 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 post).

basé sur ce tutoriel, et le 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;
    }
});                    
76
répondu Gerbus 2012-11-07 22:13:45

depuis la version 0.10.1 de Twitter Typeahead ( https://github.com/twitter/typeahead.js ), Id / Label est supporté nativement:

  $('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 à la source callback (cb). En spécifiant displayKey:' text', je dis à la bibliothèque d'utiliser la propriété' text ' pour l'auto-suggest. Lorsque le callback' typeahead:select ' est appelé, le second argument passé dans (suggestion) contient objet qui a été sélectionné.

10
répondu Johnny Oshika 2014-02-14 10:00:10

pour clarifier ce que je disais dans mon commentaire. Si vous voulez plusieurs têtes de type a sur la même page, vous devez définir chacune dans une fonction et créer une variable de carte séparée 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();
});

Note comment j'ai scopé la variable map à l'intérieur des deux fonctions d'initialisation de champ. Ceci est important, il s'assure que la même variable map n'est pas utilisée par les deux champs d'entrée.

6
répondu Michael Yagudaev 2013-04-21 23:48:36

le problème que j'ai vu avec certaines de ces solutions, est que la fonction source est appelée à plusieurs reprises sur chaque événement de la boîte de saisie. Sens, les tableaux sont construits et mis en boucle sur chaque événement keyup.

Ce n'est pas nécessaire. En utilisant une fermeture, vous pouvez traiter les données une seule fois, et maintenir une référence à partir de la fonction source . En outre, la solution suivante résout le problème d'espace de noms global de @Gerbus solution, et vous permet également de jouer avec le tableau de données une fois que l'utilisateur a sélectionné quelque chose (par exemple, en supprimant 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
répondu vcardillo 2013-04-25 08:18:52

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 que typeahead montre, et un pour l'objet dont le nom est extrait. Quand une des options est sélectionnée, elle utilise le nom pour trouver l'objet d'où il vient.

3
répondu Kevin Lawrence 2013-08-12 02:56:09

Voici une solution encapsulée. Cette solution vous permet d'avoir plus d'un type d'en-tête sur la même page.

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

$('.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, quand vous avez besoin d'obtenir la clé de l'actuel article sélectionné, vous avez juste besoin de faire $('.make-me-typeahead').data('selected')

3
répondu Capy 2017-05-23 11:54:24

j'ai été aux prises avec ce problème moi-même, voici la solution que j'ai trouvé, 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
répondu HennyH 2016-08-19 08:17:20

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
répondu Pedro Muniz 2013-01-24 11:22:03

la réponse choisie ne traite pas des étiquettes non uniques (p. ex. le nom d'une personne). J'utilise ce qui suit qui conserve le formatage par défaut du surligneur:

            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
répondu will.ogden 2013-11-05 12:15:20

j'ai fait une directive angulaire 2, typeahead-angular2 , qui fait exactement ce que vous voulez, et gère le cas des étiquettes non uniques ainsi. Vous pouvez prendre le typeahead partie.

cette directive traite les objets complexes avec plusieurs attributs et gérer le cas où l'étiquette n'est pas unique. il reçoit basicly 4 paramètres:

  • @Input() name; //nom de l'auteur
  • @Input() objectsDataSet; / / un ensemble de données d'objets , il pourrait s'agir de n'importe quel type d'objet
  • @Input() handleFunction; / / une fonction de rappel appelée lorsque l'objet est sélectionné , vous pouvez passer l'objet ou ce que vous voulez pour cette fonction.
  • @Input() labelAtt; / / l'attribut étiquette ( object[labelAtt] est affiché à l'utilisateur , il doit s'agir d'une chaîne de caractères).

exemple:

<input type="text" class="form-control" placeholder="Name..." typeaheadautocomplete [objectsDataSet]="clientList" [labelAtt]="'Firstname'" [name]="'clients'" [handleFunction]="logClient">

comme vous pouvez le voir: clientList est un tableau d'objets "client", dites {"Fistname":"Billel","Lastname":"Guerfa",....} nous utilisons le Attribut Firstname pour la liste autocomplete. logClient ici recieves un client de l'objet et l'affiche.

dépendances:

il suffit de déclarer le script Type Head à l'index.niveau de html.

voir: https://github.com/BillelGuerfa/typeahead-angular2 /

-1
répondu Billel Guerfa 2016-05-26 12:15:33