45 votes

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 utiliser un id comme une norme <select />

Voici mon initialisation de 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 type de JSON que j'envoie

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

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

79voto

Gerbus Points 799

Il y a un excellent tutoriel ici qui explique comment faire : http://tatiyants.com/how-to-use-json-objects-with-twitter-bootstrap-typeahead/ (lisez mon commentaire sur cette page s'il n'a pas encore été répercuté dans la partie principale du billet).

Sur la base de ce tutoriel 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;
    }
});

6voto

yagudaev Points 1286

Pour clarifier ce que je disais dans mon commentaire. Si vous voulez plusieurs types d'avant sur la même page, vous devez définir chacun d'eux dans une fonction et créer une variable map 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 scopé la variable map dans les deux fonctions d'initialisation du champ. C'est important, cela permet de s'assurer que la même variable map n'est pas utilisée par les deux champs de saisie.

4voto

HennyH Points 4731

J'ai moi-même été confronté à ce problème, voici la solution que j'ai trouvée, pour les données de type :

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

C'é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

            }

        });

3voto

Kevin Lawrence Points 546

La réponse choisie est un peu bidon. 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 affiche, et un pour l'objet dont le nom est extrait. Lorsque l'une des options est sélectionnée, il utilise le nom pour trouver l'objet d'où il provient.

3voto

vcardillo Points 461

Le problème que j'ai vu avec certaines de ces solutions, c'est que le source est appelée de manière répétée à chaque événement de frappe de la zone de saisie. En d'autres termes, les tableaux sont construits et bouclés à 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 à celles-ci à partir de l'objet source fonction. En outre, 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, en retirant 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);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X