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>