【问题标题】:Twitter Bootstrap Typeahead - Id & LabelTwitter Bootstrap Typeahead - 标识和标签
【发布时间】:2012-09-05 13:52:23
【问题描述】:

我正在使用 Bootstrap 2.1.1 和 jQuery 1.8.1 并尝试使用 Typeahead 的功能。

我尝试显示 label 并使用 id 就像标准的<select />

这是我的预输入初始化:

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

这是我要发送的 JSON 类型

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

我如何告诉process() 显示我的标签并将选定的 ID 存储在另一个隐藏字段中?

【问题讨论】:

标签: javascript jquery twitter-bootstrap autocomplete typeahead.js


【解决方案1】:

我自己一直在努力解决这个问题,这是我想出的解决方案,用于以下类型的数据:

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

曾经:

$("#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

            }

        });

【讨论】:

    【解决方案2】:

    我创建了一个 Angular 2 指令,typeahead-angular2,它完全符合您的要求,并且还处理了非唯一标签的情况。您可以使用预输入部分。

    该指令处理具有多个属性的复杂对象和 处理标签不唯一的情况。它基本上收到 4 参数:

    • @Input() name; //提前输入的名称
    • @Input() objectsDataSet; // 对象的数据集,可以是任何类型的对象
    • @Input() handleFunction; // 选择对象时调用的回调函数,你可以传递对象或任何你想要的 到这个函数。
    • @Input() labelAtt; // 标签属性(object[labelAtt] 显示给用户,必须是字符串)。

    示例:

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

    如您所见:clientList 是“客户”对象的数组,让我们 说{"Fistname":"Billel","Lastname":"Guerfa",....} 我们使用 自动完成列表的名字属性。 logClient 这里收到 一个客户端对象并显示它。

    依赖关系:

    只需在 index.html 级别声明 typeahead 脚本。

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

    【讨论】:

      【解决方案3】:

      这是一个封装的解决方案。此解决方案允许您在同一页面上拥有多个预输入。

      这是#13279176 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;
          }
      });
      

      现在,当您需要获取当前选定项目的密钥时,您只需执行$('.make-me-typeahead').data('selected')

      【讨论】:

        【解决方案4】:

        从 Twitter Typeahead (https://github.com/twitter/typeahead.js) 的 0.10.1 版开始,本地支持 Id / Label:

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

        如果是上面的示例,我将一个对象数组传递给源回调 (cb)。通过指定 displayKey: 'text',我告诉库使用 'text' 属性进行自动建议。当调用 'typeahead:select' 回调时,传入的第二个参数(建议)包含被选择的对象。

        【讨论】:

        • 这就是答案。
        【解决方案5】:

        所选答案不涉及非唯一标签(例如人名)。我正在使用以下保持默认荧光笔格式的内容:

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

        【讨论】:

          【解决方案6】:

          选择的答案有点小题大做。我一直在寻找同样的东西,这种方法效果很好:

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

          它保留两个数组,一个用于预先输入显示的名称,另一个用于从中提取名称的对象。选择其中一个选项时,它将使用该名称来从它来源中找到对象。

          【讨论】:

          【解决方案7】:

          我在其中一些解决方案中看到的问题是,source 函数在输入框的每个 keyup 事件上都被重复调用。意思是,数组正在构建并在每个 keyup 事件上循环。

          这不是必需的。使用闭包,您可以只处理一次数据,并在source 函数中维护对它的引用。此外,以下解决方案解决了@Gerbus 解决方案的全局命名空间问题,并且还允许您在用户选择某些内容后(例如,从列表中删除该项目)来玩数据数组。

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

          【讨论】:

            【解决方案8】:

            澄清我在评论中所说的话。如果您想在同一页面上输入多个类型,您需要在一个函数中定义每个类型并为它们创建一个单独的映射变量。

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

            注意我是如何在两个字段初始化函数中限定 map 变量的。这很重要,它确保两个输入字段不使用相同的映射变量。

            【讨论】:

              【解决方案9】:

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

              【讨论】:

                【解决方案10】:

                这里有一个很棒的教程来解释如何做到这一点:http://tatiyants.com/how-to-use-json-objects-with-twitter-bootstrap-typeahead/(如果它尚未反映在帖子的主要部分中,请阅读我在该页面上的评论)。

                根据该教程和您提供的 JSON,您可以执行以下操作:

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

                【讨论】:

                • 需要注意的重要一点是那里的map 变量及其范围。根据您的代码,它与 objects 变量一起是全局的。这意味着您在页面上一次只能有 1 个预输入,否则地图对象将在预输入框之间共享并导致奇怪的错误。要拥有多种类型的头部,您需要使用闭包并在那里定义 map 变量。
                • "您的对象的名称必须是唯一的才能正常工作。"因此,如果您要过滤可能同名的人员列表,他们的解决方案将不起作用。
                • @yayitswei 这无论如何都是没有意义的,因为用户无法识别哪个条目是正确的。
                • @MarcelBurkhard 您可以在下拉项中包含其他元素来标识条目,例如基于 id 的 Facebook 图片。
                • 如果您使用 Bloodhound 引擎,我很想看看如何做到这一点。
                猜你喜欢
                • 2018-05-25
                • 1970-01-01
                • 1970-01-01
                • 2013-03-02
                • 1970-01-01
                • 1970-01-01
                • 2013-07-20
                • 2013-02-27
                相关资源
                最近更新 更多