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 相关文章推荐
showModalDialog 和 showModelessDialog
Jan 22 Javascript
jQuery 学习6 操纵元素显示效果的函数
Feb 07 Javascript
关于js datetime的那点事
Nov 15 Javascript
用JavaScript获取DOM元素位置和尺寸大小的方法
Apr 12 Javascript
JavaScript中的类与实例实现方法
Jan 23 Javascript
JavaScript中的Math.SQRT1_2属性使用简介
Jun 14 Javascript
jQuery Validate表单验证插件的基本使用方法及功能拓展
Jan 04 Javascript
javascript 缓冲运动框架的实现
Sep 29 Javascript
详解element-ui中form验证杂记
Mar 04 Javascript
elementUI多选框反选的实现代码
Apr 03 Javascript
layui表格设计以及数据初始化详解
Oct 26 Javascript
js实现弹幕墙效果
Dec 10 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调用Oracle存储过程
2006/10/09 PHP
php数据入库前清理 注意php intval与mysql的int取值范围不同
2010/12/12 PHP
php守护进程 加linux命令nohup实现任务每秒执行一次
2011/07/04 PHP
ThinkPHP使用心得分享-分页类Page的用法
2014/05/15 PHP
微信企业转账之入口类分装php代码
2018/10/01 PHP
PHP实现Snowflake生成分布式唯一ID的方法示例
2020/08/30 PHP
prototype Element学习笔记(Element篇三)
2008/10/26 Javascript
Extjs学习笔记之六 面版
2010/01/08 Javascript
腾讯与新浪的通过IP地址获取当前地理位置(省份)的接口
2010/07/26 Javascript
nodejs实现的一个简单聊天室功能分享
2014/12/06 NodeJs
javascript瀑布流布局实现方法详解
2016/02/17 Javascript
JS判断字符串字节数并截取长度的方法
2016/03/05 Javascript
jquery注册文本框获取焦点清空,失去焦点赋值的简单实例
2016/09/08 Javascript
手机端js和html5刮刮卡效果
2020/09/29 Javascript
AngularJS  ng-repeat遍历输出的用法
2017/06/19 Javascript
Vue使用json-server进行后端数据模拟功能
2018/04/17 Javascript
Vue2.0中三种常用传值方式(父传子、子传父、非父子组件传值)
2018/08/16 Javascript
vue视频播放暂停代码
2019/11/08 Javascript
python处理Excel xlrd的简单使用
2017/09/12 Python
Python实现的径向基(RBF)神经网络示例
2018/02/06 Python
Python多进程与服务器并发原理及用法实例分析
2018/08/21 Python
聊聊python里如何用Borg pattern实现的单例模式
2019/06/06 Python
Python整数对象实现原理详解
2019/07/01 Python
TensorFlow2.0矩阵与向量的加减乘实例
2020/02/07 Python
Python实例方法、类方法、静态方法区别详解
2020/09/05 Python
python 代码运行时间获取方式详解
2020/09/18 Python
巾帼文明岗申报材料
2014/05/01 职场文书
校庆标语集锦
2014/06/25 职场文书
学习焦裕禄精神践行三严三实心得体会
2014/10/13 职场文书
基层党组织整改方案
2014/10/25 职场文书
培训通知书模板
2015/04/17 职场文书
费用申请报告范文
2015/05/15 职场文书
2019年度行政文员工作计划范本!
2019/07/04 职场文书
SQL Server数据库基本概念、组成、常用对象与约束
2022/03/20 SQL Server
电脑只能进入安全模式无法正常启动的解决办法
2022/04/08 数码科技
腾讯云服务器部署前后分离项目之前端部署
2022/06/28 Servers