在 angularjs 中集成 bootstrap typeahead
问题
在使用 typeahead 的时候,有这样一个需求,当用户选中其中一项的之后,将项目对应的 id 保存到另外一个变量中,以后在提交表单的时候,将这个 id 发送到服务器中。
但是,在 typeahead 中对于元素的操作,angularjs 没有感知到,导致不能获取最新的数据。
typeahead 中选择数据之后的事件 itemSelected
在 typeahead 中,我们可以定义一个 itemSelected 的事件处理函数,在选中一个项目之后, typeahead 会触发这个事件,我们在这个事件中可以获取当前选中的元素,对应的值,已经显示的提示文本。
这个函数的使用方式如下:
itemSelected: function (item, val, text) { console.info(val); }
集成到页面中,我们可以如下配置 typeahead 的配置参数。
var option = { ajax: { url: '@Url.Action("AjaxService")', timeout: 300, method: "post", triggerLength: 3, preDispatch: function (query) { return { type: type, query: query }; }, preProcess: function (data) { return data; } }, display: "name", val: "id", itemSelected: function (item, val, text) { console.info(item); } }; $("#tbxName").typeahead(option);
但是,在使用 angularjs 的时候,会带来一个问题,如果我们直接将这个 id 赋予某个元素,比如说输入框的话,angularjs 是不能检测到这个元素值的变化的,angularjs 通过监测元素的获取焦点,失去焦点,change 等事件来感知输入元素的变化,在通过代码直接赋值的时候,会没有这些事件的触发。
原理
我们可以通过 angularjs 的自定义指令将 typeahead 封装一下,在 itemSelected 事件触发的时候,调用 angularjs 的 $apply 发出通知,这样 angularjs 自然就可以感知到数据的变化了。
实现
为了便于已经熟悉 typeahead 的朋友,继续使用原来的方式进行配置,我们不修改配置方式。
指令的定义如下:
// 自定义指令 app.directive("typeahead", function () { var option = { restrict: "A", require: "?ngModel", scope: { option: "=typeahead" }, link: function (scope, element, attrs, ngModel) { // typeahead 的配置参数 var option = scope.option; // 将原来的 itemSelected 替换为 angularjs 的 itemSelected 函数 // 在用户选择了选项之后,通过 angularjs 执行 var itemSelected = option.itemSelected; option.itemSelected = function (item, val, text) { scope.$apply(function () { itemSelected(item, val, text); }) }; element.typeahead(option); } }; return option; });
scope 是自定义一个作用域,=typeahead 是将 typeahead 属性的值取过来,剩下的处理就比较简单了,我们将原来的 itemSelected 事件截获过来,通过 scope.$apply 进行处理,这样 angularjs 自然就可以获取数据的变化了。
元素的定义如下,增加一个名为 typeahead 的 Attribute, 值就是配置参数对象,定义在 angularjs 的模型对象上。
<input typeahead ="typeaheadOption" ng-model="petName" type="text" />
下面是对应的脚本。
// 控制器 app.controller("myController", function ($scope) { // 额外附加的参数 $scope.type = "java"; // 标准的使用方式 $scope.petName = "Cat"; $scope.petId = ""; // 使用标准的 typeahead 配置参数, 在元素上使用 typeahead Attribute 连接 $scope.typeaheadOption = { ajax: { url: '@Url.Action("AjaxService")', timeout: 300, method: "post", triggerLength: 3, preDispatch: function (query) { return { type: $scope.type, query: query }; }, preProcess: function (data) { return data; } }, display: "name", val: "id", itemSelected: function (item, val, text) { $scope.petId = val; } }; });
总结
使用 angularjs 的自定义指令,我们可以很容易地将 angularjs 之外的事件处理封装到 angularjs 之中。