BootstrapTagsInput-基礎避坑指南

1.簡介

BootstrapTagsInput是一個基於jQuery和Bootstrap.css的用於管理標籤的插件。javascript

官網在這:官網php

這個官網呢,怎麼說呢,比較簡潔。示例聊勝於無。css

最簡單的用法就是在引入jquery,和Bootstrap的前提下,在input標籤中添加 data-role="tagsinput",便可初始化。html

<input type="text" value="" data-role="tagsinput" />

緣由在於下方代碼,代碼最後一部分,寫了是如何初始化的。前端

/**
   * Initialize tagsinput behaviour on inputs and selects which have
   * data-role=tagsinput
   */
  $(function() {
    $("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput();
  });

還有一個經過 select標籤初始化的方式。並在標籤加上 multiple  屬性,在經過$("select").tagsinput('items'),獲取值時,返回的是一個數組,而不是逗號分隔的字符串了。java

<select multiple data-role="tagsinput">
  <option value="Amsterdam">Amsterdam</option>
  <option value="Washington">Washington</option>
  <option value="Sydney">Sydney</option>
  <option value="Beijing">Beijing</option>
  <option value="Cairo">Cairo</option>
</select>

以上兩個方法都會將現有value值或者option元素,自動設置爲標籤。jquery

2.Typeahead&Typeahead

通常來講,若是你只使用輸入,回車成爲標籤的功能,那你可能遇不到什麼坑。git

可是一旦你遇到了根據用戶輸入顯示相關補全/提示/預輸入的需求時,坑就來了。github

2.1 Typeahead(預先輸入)

在其文檔中,有一個介紹,就是關於Typeahead的,他是這麼說的:web

Typeahead is not included in Bootstrap 3, so you'll have to include your own typeahead library. I'd recommed typeahead.js. An example of using this is shown below.

大意是:Bootstrap 3中已經不包含Typeahead啦,若是你想用,你必須本身引入的Typeahead庫。

咱們推薦了typeahead.js。下面是使用此示例的示例。巴拉巴拉。

<input type="text" value="Amsterdam,Washington" data-role="tagsinput" />
<script>
var citynames = new Bloodhound({
  datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
  queryTokenizer: Bloodhound.tokenizers.whitespace,
  prefetch: {
    url: 'assets/citynames.json',
    filter: function(list) {
      return $.map(list, function(cityname) {
        return { name: cityname }; });
    }
  }
});
citynames.initialize();

$('input').tagsinput({
  typeaheadjs: {
    name: 'citynames',
    displayKey: 'name',
    valueKey: 'name',
    source: citynames.ttAdapter()
  }
});
</script>

-------------------------------------------------------------------------------------------------------------------------------------------------------------

而後在其文檔下方有個Options介紹表,裏面有一項關於typeahead的介紹,其中關於source的介紹是:

An array (or function returning a promise or array), which will be used as source for a typeahead.

大意是:一個數組(或返回一個承諾或數組的函數),它將用typeahead的數據源。

2.2 眼睛瞪得像銅鈴 射出閃電般的精明

說實話,tagsinput 這文檔給我一種什麼感受呢?

就是=>饒你奸似鬼!照樣喝老孃的洗腳水!

上面說了,他給了一個推薦的Typeahead示列,而後下方的option表中有其對應的描述。

若是你只是直接複製示例代碼,直接運行,你會遇到別的坑,可是我要說的是,睜大眼睛,仔細觀瞧!

在初始化tagsinput時有兩個預輸入選項,一個是typeaheadjs,另外一個是typeahead

看出區別了嗎?區別多了一個js!!!!!

2.2.1 typeaheadjs的坑

GitHub 地址

typeahead.js
Inspired by twitter.com's autocomplete search functionality, typeahead.js is a flexible JavaScript library that provides a strong foundation for building robust typeaheads.

The typeahead.js library consists of 2 components: the suggestion engine, Bloodhound, and the UI view, Typeahead. The suggestion engine is responsible for computing suggestions for a given query. The UI view is responsible for rendering suggestions and handling DOM interactions. Both components can be used separately, but when used together, they can provide a rich typeahead experience.

這裏說明了,typeaheadjs是受twitter.com自動完成搜索功能的啓發,獨立的庫。

typeahead.js庫由2個組件組成:建議引擎, Bloodhound和UI視圖,Typeahead。

而後按照tagsinput的示例代碼去作。能夠作出來。

可是例子是你顯示什麼!值就是什麼!就是說你顯示「大腦虎」,你選擇輸入「大腦虎」成爲標籤,你提交的值也是「大腦虎」這個字符串!

通常狀況下,用於搜索功能沒啥問題,由於搜的就是字符串,可是若是用於其餘地方,實際值id和顯示值name分離的怎麼辦??

例如城市選擇:

[{id: 101, name: "北京"},{id: 102, name: "南京"}]

給用戶展現的是「北京」,「南京」的字符串,實際上傳遞給後臺入庫的實際上是「id」的值。

示例中的初始化是這樣的:

$('input').tagsinput({
  typeaheadjs: {
    name: 'citynames',
    displayKey: 'name',//
    valueKey: 'name',//你把這個name換成ID他也不正常
    source: citynames.ttAdapter()
  }
});

注意啊,這個displayKey和valueKey 設置的是typeaheadjs 的顯示和實際值,和tagsinput沒有關係,顯示依然不正確!

typeaheadjs應用示列

須要引入的文件:

<link rel="stylesheet" href="./bootstrap-tagsinput.css" /> 
<script src="./jquery-3.2.1.js"></script>
<script src="./typeaheadjs.js"></script>
<script src="./bootstrap-tagsinput.js"></script>

提示欄的下拉樣式: 

<style>
      .label {
        background: #428bca;
      }
      .bootstrap-tagsinput .tag {
        color: #000;
      }

      .twitter-typeahead.tt-query,
      .twitter-typeahead .tt-hint {
        margin-bottom: 0;
      }

      .twitter-typeahead .tt-hint {
        display: none;
      }

      .tt-menu {
        position: absolute;
        top: 100%;
        left: 0;
        z-index: 1000;
        display: none;
        float: left;
        min-width: 160px;
        padding: 5px 0;
        margin: 2px 0 0;
        list-style: none;
        font-size: 14px;
        background-color: #ffffff;
        border: 1px solid #cccccc;
        border: 1px solid rgba(0, 0, 0, 0.15);
        border-radius: 4px;
        -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
        box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
        background-clip: padding-box;
        cursor: pointer;
      }

      .tt-suggestion {
        display: block;
        padding: 3px 20px;
        clear: both;
        font-weight: normal;
        line-height: 1.428571429;
        color: #333333;
        white-space: nowrap;
      }

      .tt-suggestion:hover,
      .tt-suggestion:focus {
        color: #ffffff;
        text-decoration: none;
        outline: 0;
        background-color: #428bca;
      }
    </style>

HTML:

<body>
    <div class="example">
      <h2>基本示例</h2>
      <form action="#" method="get">
        <input data-ajax="test.php"  class="typeahead" type="text" name="test" placeholder="請輸入省份" value="" />
        <button type="submit">提交</button>
      </form>
    </div>
</body>

PHP後端:

<?php 
    echo json_encode([["id"=>101,"name"=>"北京"],["id"=>102,"name"=>"南京"]]); 
?>

PHP注意事項:

一、新建一個名叫「test.php」的文件,把這個粘貼進去,放在與你script的同級目錄下。

二、要在服務器端上運行(本地的),且安裝了PHP。

js:

function tagsinputAjax(inputSelector, valueName, showName) {
      var $input = $(inputSelector),
        ajaxUrl = $input.attr("data-ajax");
      //初始化bh
      var engine = new Bloodhound({
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        datumTokenizer: Bloodhound.tokenizers.whitespace,
        remote: {
          url: ajaxUrl + "?q=%QUERY",
          wildcard: "%QUERY"
        }
      });

      engine.initialize();

      var typeaheadConfig = {
        name: valueName,
       // displayKey: valueName,
       // valueKey: showName,
        templates: {
          suggestion: function (data) { //格式化預輸
            console.log(data)
            return '<div data-id="' + data[valueName] + '">' + data[showName] + '</div>';
          }
        },
        source: engine.ttAdapter()
      };

      //初始化
      $input.tagsinput({
        itemValue: valueName,
        itemText: showName,
        typeaheadjs: typeaheadConfig
      })
    }

 tagsinputAjax(".typeahead", "id", "name");

這段作了一個簡單的封裝,指定了class爲「.typeahead」的input初始化爲tags-input,顯示的值爲「name」,實際值爲「id」。

注意事項:

1.此例子爲後臺處理查詢值,前端get方式獲取類似值。

2.數據爲動態獲取,非一次性獲取所有數據,使用建議引擎去提示(tagsinput官網給的是所有的獲取過來的)方式,而是每次輸入的提示都從後臺獲取,我作的時候總數據有3w條,一次性加載過來顯然是不合適的。

3.typeaheadjs會緩存數據的,因此在調試的時候要注意,若是未按照預期呈現結果,記得清緩存後再次調試。

4.註釋掉的displayKey 和 valueKey不會有影響,是由於格式化了suggestion條目。

5.data-ajax="test.php"寫在了input上,由於我實際作項目時,表單頁面是後臺動態生成的,根據不一樣表單設置不一樣的data-ajax能夠獲取不一樣預輸入數據。

2.2.2 typeahead--Bootstrap 3 Typeahead

GitHub官網

介紹就不貼了,本身看吧。

Bootstrap 3 Typeahead 原來是集成在Bootstrap 2裏的一個js插件,到3的時候給獨立出來了。

直接翻到GitHub官網最下面,就是介紹Bootstrap Tags Input如何使用Bootstrap 3 Typeahead。

Bootstrap Tags Input is a jQuery plugin providing a Twitter Bootstrap user interface for managing tags. Bootstrap Tags Input has a typeahead option which allows you to set the source:

$("input").tagsinput({
  typeahead: {
    source: ["Amsterdam", "Washington", "Sydney", "Beijing", "Cairo"]
  }
});

or

$("input").tagsinput({
  typeahead: {
    source: function(query) {
      return $.get("http://someservice.com");
    }
  }
});

See also: https://github.com/bassjobsen/Bootstrap-3-Typeahead/issues/40

 本地的預輸入沒有問題,有問題的是第二個,你把$.get中的地址換成你本身以後,它請求成功了,可是!它沒有提示!它沒有顯示預輸入的下拉。

這是爲啥呢?我也不知道。

查找問題

問題出現了!那就想辦法解決吧。

首先,我把代碼由:

$("input").tagsinput({
                typeahead: {
                    source: function (query) {
                        return $.get("test.php?q=" + query);
                    }
                }
            });

改成:

$("input").tagsinput({
                typeahead: {
                    source: function (query) {
                      //  return $.get("test.php?q=" + query);
                          return ["北京", "南京"];
                    }
                }
            });

結果發現可用,那說明什麼?

說明:

1.請求時成功的,而且是返回數據的,那麼請求的數據沒有問題。

2.直接return城市數據組,依然可使用,說明不是數據格式的問題。

3.問題出在了$.get()這了。

說明是返回數據的問題,我把數據返回一下,寫成這樣是否是就好了?

$("input").tagsinput({
                typeahead: {
                    source: function (query) {
                        var result=null;
                        $.ajax({
                            url: "test.php?q=" + query,
                            type: "get",
                            dataType: "json",//對的,數據類型不能少
                            success: function (res) {
                                result = res.data;
                            }
                        });
                        return result;
                    }
                }
            });

而後報錯了...

Uncaught TypeError: Cannot read property 'success' of null bootstrap-tagsinput.js:294

at Typeahead.source (bootstrap-tagsinput.js:294)

at Typeahead.<anonymous> (bootstrap3-typeahead.js:226)

查了一下 ,截取一下tagsinput.js

......
      source: function (query, process) {
            function processItems(items) {
              var texts = [];

              for (var i = 0; i < items.length; i++) {
                var text = self.options.itemText(items[i]);
                map[text] = items[i];
                texts.push(text);
              }
              process(texts);
            }

            this.map = {};
            var map = this.map,
                data = typeahead.source(query);// 這裏的data就是返回的數據
            if ($.isFunction(data.success)) {//報錯的是這裏
              // support for Angular callbacks
              data.success(processItems);
            } else if ($.isFunction(data.then)) {
              // support for Angular promises
              data.then(processItems);
            } else {
              // support for functions and jquery promises
              $.when(data)
               .then(processItems);
            }
          },
......

打印了一下data爲null,因此報錯。爲啥呢?爲返回的數據是null。network裏顯示xhr請求時成功的啊,也返回了數據啊?

其實改一下代碼就行了:

$("input").tagsinput({
                typeahead: {
                    source: function (query) {
                        var result=null;
                        $.ajax({
                            url: "test.php?q=" + query,
                            type: "get",            
                            dataType: "json",//對的,數據類型不能少
                            async: false,//新增了一個async爲false
                            success: function (res) {
                                result = res.data;
                            }
                        });
                        return result;
                    }
                }
            });

而後就OK了。爲啥這就OK了呢?本身想一下,很簡單的。

若是仍然想用顯示和值分離,則以下:

$("input").tagsinput({
                itemValue: '此處填寫實際的要傳遞給後臺的key',
                itemText: '此處填寫顯示給用戶看的的key',
                typeahead: {
                    source: function (query) {
                        var result=null;
                        $.ajax({
                            url: "test.php?q=" + query,
                            type: "get",
                            async: false,
                            success: function (res) {
                                result = res.data;
                            }
                        });
                        return result;
                    }
                }
            });

php:

<?php 

    echo json_encode(["status" => "OK", "data" => [
         ["id"=>101,"name"=>"北京"],
         ["id"=>102,"name"=>"南京"]
    ]]) 
 
?>

注意:

3.總結

我此次作項目用到了tagsinput.js,這貨的手冊作的特別的不友好,示列也不許確。基本上初次用的人都會遇到各類各樣的問題,我爲了解決各類問題各類google。心累,不愛。

typeahead和typeaheadjs傻傻分不清楚。

tagsinput.js源碼中有註釋,看到沒,它去判斷設置中用的是Bootstrap 3 Typeahead的預輸入仍是用的typeaheadjs。

更操蛋的是,它重寫了

Bootstrap tagsinput use the source parameter only. When using this typeahead option bootstrap tagsinput initiates a new typeahead class: self.$input.typeahead. Bootstrap tagsinput's typeahead classes overwrite the original source function and has a call to process already as shown below:

Bootstrap tagsinput僅使用source參數。使用此typeahead選項時,bootstrap tagsinput會啓動一個新typeahead類:self.$input.typeahead。Bootstrap tagsinput的typeahead類覆蓋原始源函數,而且已經調用process,以下所示:

if (self.options.typeahead) {
        var typeahead = self.options.typeahead || {};

        makeOptionFunction(typeahead, 'source');

        self.$input.typeahead($.extend({}, typeahead, {
          source: function (query, process) {
            function processItems(items) {
              var texts = [];

              for (var i = 0; i < items.length; i++) {
                var text = self.options.itemText(items[i]);
                map[text] = items[i];
                texts.push(text);
              }
              process(texts);
            }

            this.map = {};
            var map = this.map,
                data = typeahead.source(query);
            if ($.isFunction(data.success)) {
              // support for Angular callbacks
              data.success(processItems);
            } else if ($.isFunction(data.then)) {
              // support for Angular promises
              data.then(processItems);
            } else {
              // support for functions and jquery promises
              $.when(data)
               .then(processItems);
            }
          },
          updater: function (text) {
            self.add(this.map[text]);
            return this.map[text];
          },
          matcher: function (text) {
            return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1);
          },
          sorter: function (texts) {
            return texts.sort();
          },
          highlighter: function (text) {
            var regex = new RegExp( '(' + this.query + ')', 'gi' );
            return text.replace( regex, "<strong>$1</strong>" );
          }
        }));
      }

生活啊 !坑爹啊!

相關文章
相關標籤/搜索