不肯定度指令,传入参量类别,而后该指令列出该类别下的全部不肯定度。javascript
新增页面用到了三个该指令,只有最后一个成功,前两个都没有数据。html
如下是指令源码:java
'use strict'; /** * @ngdoc directive * @name webappApp.directive:yunzhiAccuracyUncertainty * @description * # yunzhiAccuracyUncertainty * 不肯定度指令 * zhangxishuo */ angular.module('webappApp') .directive('yunzhiAccuracyUncertainty', function($filter) { return { templateUrl: 'views/directive/yunzhiAccuracyUncertainty.html', restrict: 'E', scope: { parameterCategory: '=', // 参量类别 ngModel: '=' // 不肯定度 }, link: function postLink(scope, element, attrs) { var self = this; // 初始化 self.init = function() { // 初始化不肯定度空列表 scope.accuracyList = []; // 监听参量类别 scope.$watch('parameterCategory', self.watchParameterCategory); // 监听不肯定度 scope.$watch('ngModel', self.watchNgModel); }; // 监听参量类别 self.watchParameterCategory = function(newValue) { if (newValue && newValue.id) { // 设置不肯定度列表 scope.accuracyList = newValue.accuracyUncertaintyList; // 过滤数据 self.filter(); } }; // 监听不肯定度 self.watchNgModel = function(newValue) { if (newValue && newValue.id) { // 设置默认选中 scope.selected = newValue; } }; // 过滤数据 self.filter = function() { angular.forEach(scope.accuracyList, function(accuracy) { // 过滤不肯定度 accuracy._value = $filter('yunzhiAccuracyWithUnit')(accuracy); }); }; // 更新模型 self.updateModel = function(selected) { // 更新数据 scope.ngModel = selected; }; // 传给视图 scope.updateModel = self.updateModel; self.init(); } }; });
尝试打印了一下scope.accuracyList
,果真有问题。angularjs
前两个都是空,最后一个数组有值。web
想不明白,这里明明监听参量类别,并将scope
的accuracyList
设置了值啊?为何没有呢?bootstrap
尝试打印一下scope
。数组
去关注scope
的$id
就好了。浏览器
依次打印的是:app
504 508 // 第一个指令 506 508 // 第二个指令 508 508 // 第三个指令
前两个指令执行时赋值的是一个scope
,而过滤的又是另外一个scope
,因此过滤不出数据,最后一个是同一scope
,因此正常输出。webapp
HTML Compiler
容许开发者教会浏览器一些新的语法,AngularJS
称这个为指令。
Compiler
是一个遍历DOM
去搜寻属性的AngularJS
服务,编译分为如下两个阶段。
Compile
:遍历DOM
并收集全部的指令,返回结果是一个linking
函数。Link
:使用scope
整合指令并产生动态视图,任何scope
模型上的改变都会反映到视图上,任何视图上的用户交互也会反映到scope
模型上。AngularJS
操做DOM
节点而不是字符串,这很重要。但一般,你不须要关注这个,由于当页面加载时,浏览器会自动把HTML
转换为DOM
。
指令编译有如下三阶段:
$compile
遍历DOM
并匹配指令,若是compiler
发现有匹配指令的元素,就会将该指令添加到指令列表中。一个元素可能匹配多个指令。DOM
元素的指令都被肯定,而后compiler
会根据优先级对指令进行排序。每个指令的compile
函数都会被执行,每个compile
函数都有操做DOM
的机会。compile
会返回link
函数,这些函数被组合成一个“组合的”link
函数,它能调用每一个指令返回的link
函数。$compile
会调用上一步中的“组合的”link
函数来连接scope
和模板。下面是官方的示意代码:
// HTML字符串 var html = '<div ng-bind="exp"></div>'; // 将HTML字符串转换为DOM模板 var template = angular.element(html); // 编译DOM模板返回link函数 var linkFn = $compile(template); // 将编译后的模板与scope连接 var element = linkFn(scope); // 添加到DOM中 parent.appendChild(element);
compile
只在编译时执行一次,只要页面中存在一个该指令,该指令的link
方法就执行一次。
因此,AngularJS
使用$compile
编译个人指令,而后看我页面中用到了三个该指令,而且都是独立scope
,因此就建立了三个scope
。
而后使用这三个scope
去调用link
函数。
前面已经提到,AngularJS
会将link
函数统一组合成一个“组合的”link
函数,因此咱们能够猜测,组合函数中的link
函数的数量与指令的数量一致,因此三次调用的是一个link
函数,link
函数只有一个实例!
linkFn(scope)
将scope
传进去做为link
函数的入参。
上面的事件监听都是没毛病的,将传入的scope
绑定到视图,而后添加到DOM
中,而后就与这个link
函数无关了。
可是这个filter
就不行了。
第一个scope
调用,filter
功能是过滤第一个scope
的accuracyList
,第二个scope
调用,filter
功能是过滤第二个scope
的accuracyList
。
因此第三次执行时,第三个scope
将以前的两个都覆盖了,link
函数中的filter
的做用变成了过滤最后一个scope
的accuracyList
。
<!-- 不肯定度 --> <ui-select ng-model="selected" theme="bootstrap" ng-change="updateModel(selected)"> <ui-select-match placeholder="请选择"> {{ $select.selected._value }} </ui-select-match> <ui-select-choices repeat="accuracy in accuracyList"> <div ng-bind-html="accuracy._value"></div> </ui-select-choices> </ui-select>
因此这里下拉框显示的是不肯定度过滤后的_value
的值,这里的空字符串看起来不明显,加上test
测试一下。
因此,这块视图绑定的scope
是正确的,只是时间监听以后去过滤数据,由于过滤的并非当前scope
的数据,因此accuracy._value
就没有值,是undefined
,因此显示一个空的字符串。
明白了原理以后解决问题天然易如反掌,只需将filter
与scope
独当即可,这样就不受每次执行不一样scope
的影响了。
不少东西,书上是没有的,须要咱们本身去发现,去分析,去解决。
翻开了以前遇到指令编译问题时从别人博客里学习来的手动编译方法。
angular.module('webappApp') .directive('reCompile', function($compile) { return { restrict: 'A', link: function postLink(scope, element, attrs) { // 监听使用该指令的元素上的ngBindHtml attrs.$observe('ngBindHtml', function() { // 若是元素使用了ngBindHtml指令 if (attrs.ngBindHtml) { // 从新编译 $compile(element[0].children)(scope); } }); } }; });
记得以前的需求是,数据通过过滤器过滤,返回的是一段HTML
代码,虽然使用ng-bind-html
能将该段代码添加到DOM
中,可是这段代码中有指令,由于该指令不是初始时就有的,因此,这个指令是不会被编译的。
因此须要编写一个从新编译的指令,手动编译动态建立的指令。
记得当时,看这段代码也不是那么彻底理解,如今学习完指令的编译以后,再去翻看以前的代码,一切原来是如此简单。
思想与能力,是须要时间沉淀的。