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 相关文章推荐
javascript 多级checkbox选择效果
Aug 20 Javascript
javascript 获取表单file全路径
Dec 31 Javascript
在JavaScript中获取请求的URL参数
Dec 22 Javascript
jquery弹出关闭遮罩层实例
Aug 06 Javascript
jQuery关于导航条背景切换效果实现示例
Sep 04 Javascript
Node.js模拟浏览器文件上传示例
Mar 26 Javascript
详谈JS中实现种子随机数及作用
Jul 19 Javascript
vue按需引入element Transfer 穿梭框
Sep 30 Javascript
小程序文字跑马灯效果
Dec 28 Javascript
vue.js购物车添加商品组件的方法
Sep 17 Javascript
解决Vue大括号字符换行踩的坑
Nov 09 Javascript
Vue中强制组件重新渲染的正确方法
Jan 03 Vue.js
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表单转换textarea换行符的方法
2010/09/10 PHP
判断php数组是否为索引数组的实现方法
2013/06/13 PHP
php调整gif动画图片尺寸示例代码分享
2013/12/05 PHP
php获取客户端IP及URL的方法示例
2017/02/03 PHP
在Ajax中使用Flash实现跨域数据读取的实现方法
2010/12/02 Javascript
六款帮助你实现惊艳视差滚动效果的jQuery插件
2012/09/14 Javascript
用js来获取上传的文件名纯粹是为了美化而用
2013/10/23 Javascript
js substring从右边获取指定长度字符串(示例代码)
2013/12/23 Javascript
Javascript 函数parseInt()转换时出现bug问题
2014/05/20 Javascript
JS获取屏幕高度的简单实现代码
2016/05/24 Javascript
深入分析javascript中的错误处理机制
2016/07/17 Javascript
原生JS实现图片轮播切换效果
2016/12/15 Javascript
实例解析js中try、catch、finally的执行规则
2017/02/24 Javascript
JavaScript实现的选择排序算法实例分析
2017/04/14 Javascript
Vue如何实现组件的源码解析
2017/06/08 Javascript
基于jQuery中ajax的相关方法汇总(必看篇)
2017/11/08 jQuery
js实现的格式化数字和金额功能简单示例
2019/07/30 Javascript
Vue Router的手写实现方法实现
2020/03/02 Javascript
javascript实现简易数码时钟
2020/03/30 Javascript
[43:49]LGD vs CHAOS 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/16 DOTA
[01:28]一分钟告诉你DOTA2 TI9不朽宝藏Ⅱ中有什么!
2019/07/09 DOTA
Python兔子毒药问题实例分析
2015/03/05 Python
Python标准库urllib2的一些使用细节总结
2015/03/16 Python
设计模式中的原型模式在Python程序中的应用示例
2016/03/02 Python
用tensorflow搭建CNN的方法
2018/03/05 Python
Django forms表单 select下拉框的传值实例
2019/07/19 Python
节省高达65%的城市景点费用:Go City
2019/07/06 全球购物
Ray-Ban雷朋瑞典官方网站:全球领先的太阳眼镜品牌
2019/08/22 全球购物
介绍一下linux文件系统分配策略
2012/11/17 面试题
股权投资意向书
2014/04/01 职场文书
听课评语大全
2014/04/30 职场文书
竞选班干部演讲稿100字
2014/08/20 职场文书
群众路线对照检查材料
2014/09/22 职场文书
2014年防汛工作总结
2014/12/08 职场文书
乡镇一岗双责责任书
2015/01/29 职场文书
团组织关系介绍信
2019/06/24 职场文书