AngularJS 多指令Scope问题的解决


Posted in Javascript onOctober 25, 2018

问题描述

不确定度指令,传入参量类别,然后该指令列出该类别下的所有不确定度。

新增页面用到了三个该指令,只有最后一个成功,前两个都没有数据。

AngularJS 多指令Scope问题的解决

AngularJS 多指令Scope问题的解决

AngularJS 多指令Scope问题的解决

探究源码

以下是指令源码:

'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 多指令Scope问题的解决

前两个都是空,最后一个数组有值。

AngularJS 多指令Scope问题的解决

想不明白,这里明明监听参量类别,并将scopeaccuracyList设置了值啊?为什么没有呢?

AngularJS 多指令Scope问题的解决

scope

尝试打印一下scope

AngularJS 多指令Scope问题的解决

AngularJS 多指令Scope问题的解决

AngularJS 多指令Scope问题的解决

去关注scope$id就行了。

依次打印的是:

504
508   // 第一个指令
506
508   // 第二个指令
508
508   // 第三个指令

前两个指令执行时赋值的是一个scope,而过滤的又是另一个scope,所以过滤不出数据,最后一个是同一scope,所以正常输出。

原因

官方文档

HTML Compiler - AngularJS

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函数的入参。

AngularJS 多指令Scope问题的解决

上面的事件监听都是没毛病的,将传入的scope绑定到视图,然后添加到DOM中,然后就与这个link函数无关了。

AngularJS 多指令Scope问题的解决

但是这个filter就不行了。

第一个scope调用,filter功能是过滤第一个scopeaccuracyList,第二个scope调用,filter功能是过滤第二个scopeaccuracyList

所以第三次执行时,第三个scope将之前的两个都覆盖了,link函数中的filter的作用变成了过滤最后一个scopeaccuracyList

AngularJS 多指令Scope问题的解决

<!-- 不确定度 -->
<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测试一下。

AngularJS 多指令Scope问题的解决

AngularJS 多指令Scope问题的解决

所以,这块视图绑定的scope是正确的,只是时间监听之后去过滤数据,因为过滤的并不是当前scope的数据,所以accuracy._value就没有值,是undefined,所以显示一个空的字符串。

AngularJS 多指令Scope问题的解决

解决方案

明白了原理之后解决问题自然易如反掌,只需将filterscope独立即可,这样就不受每次执行不同scope的影响了。

AngularJS 多指令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中,但是这段代码中有指令,因为该指令不是初始时就有的,所以,这个指令是不会被编译的。

所以需要编写一个重新编译的指令,手动编译动态创建的指令。

记得当时,看这段代码也不是那么完全理解,现在学习完指令的编译之后,再去翻看之前的代码,一切原来是如此简单。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javscript对象原型的一些看法
Sep 19 Javascript
JS对select控件option选项的增删改查示例代码
Oct 21 Javascript
jQuery的:parent选择器定义和用法
Jul 01 Javascript
AngularJS快速入门
Apr 02 Javascript
JQuery异步加载PartialView的方法
Jun 07 Javascript
jQuery内存泄露解决办法
Dec 13 Javascript
微信小程序中hidden不生效原因的解决办法
Apr 26 Javascript
JS加密插件CryptoJS实现的DES加密示例
Aug 16 Javascript
对vux点击事件的优化详解
Aug 28 Javascript
vue.js循环radio的实例
Nov 07 Javascript
jQuery 隐藏/显示效果函数用法实例分析
May 20 jQuery
创建与框架无关的JavaScript插件
Dec 01 Javascript
jQuery+Datatables实现表格批量删除功能【推荐】
Oct 24 #jQuery
webpack打包非模块化js的方法
Oct 24 #Javascript
如何实现一个webpack模块解析器
Oct 24 #Javascript
vue项目中使用Svg的方法
Oct 24 #Javascript
js中获取URL参数的共用方法getRequest()方法实例详解
Oct 24 #Javascript
小程序云开发初探(小结)
Oct 24 #Javascript
vue-cli V3.0版本的使用详解
Oct 24 #Javascript
You might like
PHP中的reflection反射机制测试例子
2014/08/05 PHP
ThinkPHP调用common/common.php函数提示错误function undefined的解决方法
2014/08/25 PHP
如何使用PHP给图片加水印
2016/10/12 PHP
PHP载入图像imagecreatefrom_gif_jpeg_png系列函数用法分析
2016/11/14 PHP
PHP实现将base64编码字符串转换成图片示例
2018/06/22 PHP
使用Rancher在K8S上部署高性能PHP应用程序的教程
2020/07/10 PHP
ArrayList类(增强版)
2007/04/04 Javascript
node在两个div之间移动,用ztree实现
2013/03/06 Javascript
JS无限极树形菜单,json格式、数组格式通用示例
2013/07/30 Javascript
javascript读取xml实现javascript分页
2013/12/13 Javascript
Javascript判断图片尺寸大小实例分析
2014/06/16 Javascript
node.js中的emitter.emit方法使用说明
2014/12/10 Javascript
jquery制作多功能轮播图插件
2015/04/02 Javascript
JavaScript实现列表分页功能特效
2015/05/15 Javascript
Vue.js中用v-bind绑定class的注意事项
2016/12/13 Javascript
微信小程序 支付功能开发错误总结
2017/02/21 Javascript
Bootstrap的popover(弹出框)在append后弹不出(失效)
2017/02/27 Javascript
原生js实现可拖拽效果
2017/02/28 Javascript
详解webpack+express多页站点开发
2017/12/22 Javascript
Vue实现点击后文字变色切换方法
2018/02/11 Javascript
解决vue select当前value没有更新到vue对象属性的问题
2018/08/30 Javascript
Javascript var变量删除原理及实现
2020/08/26 Javascript
原生js+css实现tab切换功能
2020/09/17 Javascript
Python3中的真除和Floor除法用法分析
2016/03/16 Python
详解Python中的四种队列
2018/05/21 Python
python字典的遍历3种方法详解
2019/08/10 Python
Python人工智能之路 之PyAudio 实现录音 自动化交互实现问答
2019/08/13 Python
Python基于字典实现switch case函数调用
2020/07/22 Python
利用纯html5绘制出来的一款非常漂亮的时钟
2015/01/04 HTML / CSS
天猫精选:上天猫,就够了
2016/09/21 全球购物
应届护士推荐信
2013/11/16 职场文书
报告会主持词
2014/04/02 职场文书
生日庆典策划方案
2014/06/02 职场文书
计算机系统管理员求职信
2014/06/20 职场文书
纪念一二九运动演讲稿
2014/09/16 职场文书
致接力运动员加油稿
2015/07/21 职场文书