1.简介
BootstrapTagsInput是一个基于jQuery和Bootstrap.css的用于管理标签的插件。
官网在这:官网
这个官网呢,怎么说呢,比较简洁。示例聊胜于无。
最简单的用法就是在引入jquery,和Bootstrap的前提下,在input标签中添加 data-role="tagsinput",即可初始化。
<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'),获取值时,返回的是一个数组,而不是逗号分隔的字符串了。
<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元素,自动设置为标签。
2.Typeahead&Typeahead
一般来说,如果你只使用输入,回车成为标签的功能,那你可能遇不到什么坑。
但是一旦你遇到了根据用户输入显示相关补全/提示/预输入的需求时,坑就来了。
2.1 Typeahead(预先输入)
在其文档中,有一个介绍,就是关于Typeahead的,他是这么说的:
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的坑
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注意事项:
1、新建一个名叫“test.php”的文件,把这个粘贴进去,放在与你script的同级目录下。
2、要在服务器端上运行(本地的),且安装了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
介绍就不贴了,自己看吧。
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 newtypeahead
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>" );
}
}));
}
生活啊 !坑爹啊!
来源:oschina
链接:https://my.oschina.net/u/2598213/blog/2979092