Bootstrap typeahead插件是用來完成輸入框的自動完成、模糊搜索和建議提示的功能,支持ajax數據加載,相似於jquery的流行插件Autocomplete。javascript
typeahead的使用方式有兩種:經過數據屬性字段的方式和經過Javascript加載的方式。html
在輸入文本框input組件裏添加data-provide="typeahead"
這個屬性字段表示使用typeahead擴展插件:java
<input type="text" data-provide="typeahead">
也能夠經過設置autocomplete="off"
來避免瀏覽器本身的自動完成功能,防止與插件使用相混。jquery
經過手動的在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); } }); }
結果是能夠行得通的,以下圖所示:瀏覽器
但這裏有個問題:
在提交表單時,咱們後臺須要傳入的是話題的id,而不是搜索框裏顯示的話題name。這裏經過typeahead獲取的只有name,上面寫的ajax函數裏從後臺傳來的數據也只有name列表,該怎麼辦呢?
不難想到有下面兩個解決方案:
- 在提交的時候直接把話題name傳過去,而後在後臺處理邏輯裏再經過name搜索其對應的id。
- 將話題id和name綁定後一塊兒傳到頁面,而後在話題輸入框下面放置一個隱藏的話題id輸入框。在搜索時只須要name做爲數據源,在選取某個name後,將其對應的id值放到隱藏的id輸入框裏面。
方案1處理起來很簡單,但同時也很low,並且可能由於頁面多傳了個空格什麼的致使數據庫搜索失敗,容易出錯。
方案2看起來挺不錯的,可怎麼實現呢?如何在選取某個搜索值後作其它的操做?source函數作不到這點。看一下上面選項表中的幾個函數,其中有一個updater方法,該方法用來返回最終選取的某個值,顧名思義,咱們也能夠在方法返回以前作更新動做,好比設置某個輸入框的值。但有幾個問題:
在參考資料的幫助下,我看了下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>