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 相关文章推荐
当jQuery遭遇CoffeeScript的时候 使用分享
Sep 17 Javascript
JS Jquery 遍历,筛选页面元素 自动完成(实现代码)
Jul 08 Javascript
JS解析XML文件和XML字符串详解
Apr 17 Javascript
window.onload与$(document).ready()的区别分析
May 30 Javascript
在ASP.NET MVC项目中使用RequireJS库的用法示例
Feb 15 Javascript
vue插件vue-resource的使用笔记(小结)
Aug 04 Javascript
使用Vue.js和Flask来构建一个单页的App的示例
Mar 21 Javascript
vue中前进刷新、后退缓存用户浏览数据和浏览位置的实例讲解
Sep 21 Javascript
微信小程序实现购物页面左右联动
Feb 15 Javascript
elementUi vue el-radio 监听选中变化的实例代码
Jun 28 Javascript
jquery获取并修改触发事件的DOM元素示例【基于target 属性】
Oct 10 jQuery
JS造成内存泄漏的几种情况实例分析
Mar 02 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 VS ASP
2006/10/09 PHP
php 获取今日、昨日、上周、本月的起始时间戳和结束时间戳的方法
2013/09/28 PHP
Windows下的PHP安装pear教程
2014/10/24 PHP
php简单实现快速排序的方法
2015/04/04 PHP
PHP实现文件上传与下载实例与总结
2016/03/13 PHP
ecshop适应在PHP7的修改方法解决报错的实现
2016/11/01 PHP
js 无提示关闭浏览器页面的代码
2010/03/09 Javascript
jQuery实现简单的抽奖游戏
2017/05/05 jQuery
浅谈nodejs中的类定义和继承的套路
2017/07/26 NodeJs
Extjs 中的 Treepanel 实现菜单级联选中效果及实例代码
2017/08/22 Javascript
详解Vue中一种简易路由传参办法
2017/09/15 Javascript
在Debian(Raspberry Pi)树莓派上安装NodeJS的教程详解
2017/09/19 NodeJs
解决在Bootstrap模糊框中使用WebUploader的问题
2018/03/22 Javascript
vue-cli 3.0 自定义vue.config.js文件,多页构建的方法
2018/09/19 Javascript
JS正则表达式封装与使用操作示例
2019/05/15 Javascript
Vue filter 过滤当前时间 实现实时更新效果
2019/12/20 Javascript
浅谈Vue 函数式组件的使用技巧
2020/06/16 Javascript
[01:32]2016国际邀请赛中国区预选赛CDEC战队教练采访
2016/06/26 DOTA
用python制作游戏外挂
2018/01/04 Python
对Python subprocess.Popen子进程管道阻塞详解
2018/10/29 Python
PyQt QListWidget修改列表项item的行高方法
2019/06/20 Python
Pytorch中.new()的作用详解
2020/02/18 Python
Python生成六万个随机,唯一的8位数字和数字组成的随机字符串实例
2020/03/03 Python
python+opencv边缘提取与各函数参数解析
2020/03/09 Python
浅谈django 模型类使用save()方法的好处与注意事项
2020/03/28 Python
numpy矩阵数值太多不能全部显示的解决
2020/05/14 Python
基于python实现计算两组数据P值
2020/07/10 Python
详解CSS3中常用的样式【基本文本和字体样式】
2020/10/20 HTML / CSS
澳大利亚窗帘商店:Curtain Wonderland
2019/12/01 全球购物
大学生找工作推荐信范文
2013/11/28 职场文书
爱国主义教育活动总结
2014/05/07 职场文书
人民的好儿女观后感
2015/06/18 职场文书
驻村工作简报
2015/07/20 职场文书
4种非常实用的python内置数据结构
2021/04/28 Python
详解Python魔法方法之描述符类
2021/05/26 Python
python神经网络ResNet50模型
2022/05/06 Python