Bootstrap typeahead使用問題記錄及解決方案(轉載)

簡單介紹

Bootstrap typeahead插件是用來完成輸入框的自動完成、模糊搜索和建議提示的功能,支持ajax數據加載,相似於jquery的流行插件Autocomplete。javascript

typeahead的使用方式有兩種:經過數據屬性字段的方式和經過Javascript加載的方式。html

1. 經過屬性字段的方式

在輸入文本框input組件裏添加data-provide="typeahead"這個屬性字段表示使用typeahead擴展插件:java

<input type="text" data-provide="typeahead">

也能夠經過設置autocomplete="off"來避免瀏覽器本身的自動完成功能,防止與插件使用相混。jquery

2. 經過Javascript加載的方式

經過手動的在js中調用typeahead函數:ajax

$('.typeahead').typeahead()

屬性選項

具體數據相關的配置經過幾個選項字段和函數控制,以下表所示:sql

 

名稱 類型 默認值 描述
source array, function [ ] 提供查詢的數據源,能夠是一個字符串數組或者一個方法,該方法有兩個參數:query 輸入值和 process回調函數,回調函數能夠在返回數據源的時候調用,以將數據處理成typeahead能識別的標準數據源。
items number 8 顯示在下拉菜單中的列表數量的最大值
minLength number 1 觸發autocomplete功能所需的最少輸入字符個數
matcher function case insensitive 該方法用來肯定一個query怎麼匹配一個item,又一個入參item,表示與query匹配的實例,能夠使用this.query來引用當前的query參數,若是query匹配成功則返回true。
sorter function exact match, case sensitive, case insensitive 該方法用於給數據源排序,有一個入參items,表示typeahead數據源實例,能夠使用this.query來引用當前的query參數。
updater function returns selected item 該方法用於返回選擇的搜索項,有一個入參item,表示typeahead數據源中返回的單個實例。
highlighter function highlights all default matches 該方法用於高亮選取最終的選擇項,又一個入參item,表示typeahead數據源中返回的單個實例,返回值是一個html。

 


​具體的使用實例能夠自行Google或借鑑我最後列出的幾個參考資料。數據庫

問題記錄

這篇文章主要記錄本身初次使用typeahead時遇到的難題,以及最終的解決方法,但願能給遇到相似問題的小夥伴們一個有用的參考。json

首先,個人業務需求是輸入一個話題topic,該話題的數據源是從後臺數據庫獲取的,須要支持模糊搜索(至於左模糊、右模糊仍是全模糊,就看查詢數據庫時的sql語句怎麼寫了),所以必須使用ajax異步加載的方式獲取數據,因而寫了下面這樣一個ajax函數提供數據源:數組

source: function (query, process) { return $.ajax({ url: '/showoff/watermark/fetchTopics', type: 'post', data: {topicName: query}, dataType: 'json', success: function (result) { // 這裏省略resultList的處理過程,處理後resultList是一個字符串列表, // 通過process函數處理後成爲能被typeahead支持的字符串數組,做爲搜索的源 return process(resultList); } }); }

結果是能夠行得通的,以下圖所示:瀏覽器


2

但這裏有個問題:
在提交表單時,咱們後臺須要傳入的是話題的id,而不是搜索框裏顯示的話題name。這裏經過typeahead獲取的只有name,上面寫的ajax函數裏從後臺傳來的數據也只有name列表,該怎麼辦呢?

不難想到有下面兩個解決方案:

  1. 在提交的時候直接把話題name傳過去,而後在後臺處理邏輯裏再經過name搜索其對應的id。
  2. 將話題id和name綁定後一塊兒傳到頁面,而後在話題輸入框下面放置一個隱藏的話題id輸入框。在搜索時只須要name做爲數據源,在選取某個name後,將其對應的id值放到隱藏的id輸入框裏面。

方案1處理起來很簡單,但同時也很low,並且可能由於頁面多傳了個空格什麼的致使數據庫搜索失敗,容易出錯。

方案2看起來挺不錯的,可怎麼實現呢?如何在選取某個搜索值後作其它的操做?source函數作不到這點。看一下上面選項表中的幾個函數,其中有一個updater方法,該方法用來返回最終選取的某個值,顧名思義,咱們也能夠在方法返回以前作更新動做,好比設置某個輸入框的值。但有幾個問題:

  • topic id和name如何進行綁定?以對象的方式仍是map的方式?
  • 在返回數據源時是隻返回name列表用於搜索,仍是返回name和id的組合列表?若是隻返回name列表,那麼在updater函數裏是取不到與name對應的id值的;若是返回組合列表,那麼搜索時顯示的就不只是name了。
  • 是否是能夠在數據源裏返回綁定的組合列表,而後自定義搜索匹配方式,item只顯示name呢?

在參考資料的幫助下,我看了下typeahead js庫裏關於上面選項表裏幾個方法的默認實現,最終獲得瞭解決方案:

  • 後臺將topic id和topic name以對象列表的形式傳過來,到了ajax裏進行解析處理,獲得一個id和name組合的json字符串數組,經過process函數處理後返回。
  • 重寫matcher、sorter、highlighter和updater這四個方法,將原來裏面的item實例所有變成item.name實例,表示要經過name進行搜索匹配、高亮和排序,而與id五官。
  • 最後,在updater方法裏將topic id的隱藏輸入框的值更新爲item.id值便可。

按照上面的思路最終實現以下,這裏貼上完整的typeahead相關的代碼:

typeahead輸入框:

<input type="text" id="topicInput" name="topicName" placeholder="請輸入話題" autocomplete="off" data-provide="typeahead" />

隱藏的topic id輸入框:

<form:hidden id="topicId" name="topicId" path="labelId"/>

最終的typeahead實現js:

<script type="text/javascript"> $('#topicInput').typeahead({ source: function (query, process) { return $.ajax({ url: '/showoff/watermark/fetchTopics', type: 'post', data: {topicName: query}, dataType: 'json', success: function (result) { // 這裏的數據解析根據後臺傳入格式的不一樣而不一樣 if(result.code == "1") { var json = JSON.parse(result.data.data); var resultList = json.topicList.map(function (item) { var aItem = { id: item.id, name: item.displayName }; return JSON.stringify(aItem); }); return process(resultList); } else { alert(result.msg); } } }); }, matcher: function (obj) { var item = JSON.parse(obj); return ~item.name.toLowerCase().indexOf(this.query.toLowerCase()) }, sorter: function (items) { var beginswith = [], caseSensitive = [], caseInsensitive = [], item; while (aItem = items.shift()) { var item = JSON.parse(aItem); if (!item.name.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(JSON.stringify(item)); else if (~item.name.indexOf(this.query)) caseSensitive.push(JSON.stringify(item)); else caseInsensitive.push(JSON.stringify(item)); } return beginswith.concat(caseSensitive, caseInsensitive) }, highlighter: function (obj) { var item = JSON.parse(obj); var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&') return item.name.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) { return '<strong>' + match + '</strong>' }) }, updater: function (obj) { var item = JSON.parse(obj); $('#topicId').attr('value', item.id); return item.name; } }) </script>
相關文章
相關標籤/搜索