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 相关文章推荐
js对象的比较
Feb 26 Javascript
读jQuery之二(两种扩展)
Jun 11 Javascript
DWR实现模拟Google搜索效果实现原理及代码
Jan 30 Javascript
JavaScript 上万关键字瞬间匹配实现代码
Jul 07 Javascript
采用call方式实现js继承
May 20 Javascript
jQuery+PHP实现动态数字展示特效
Mar 14 Javascript
JS访问SWF的函数用法实例
Jul 01 Javascript
AngularJs Managing Service Dependencies详解
Sep 02 Javascript
原生JS实现幻灯片
Feb 22 Javascript
关于Node.js中Buffer的一些你可能不知道的用法
Mar 28 Javascript
详解Vue爬坑之vuex初识
Jun 14 Javascript
在vue中使用jsonp进行跨域请求接口操作
Oct 29 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 删除文件与文件夹操作 unlink()与rmdir()这两个函数的使用
2011/07/17 PHP
探讨:array2xml和xml2array以及xml与array的互相转化
2013/06/24 PHP
使用 PHPStorm 开发 Laravel
2015/03/24 PHP
PHP面向对象详解(三)
2015/12/07 PHP
php中通用的excel导出方法实例
2017/12/30 PHP
基于php双引号中访问数组元素报错的解决方法
2018/02/01 PHP
Thinkphp 3.2框架使用Redis的方法详解
2019/10/24 PHP
jquery BS,dialog控件自适应大小
2009/07/06 Javascript
jquery 元素相对定位代码
2010/10/15 Javascript
JS/jQuery实现默认显示部分文字点击按钮显示全部内容
2013/05/13 Javascript
利用ajaxfileupload插件实现文件上传无刷新的具体方法
2013/06/08 Javascript
理解Javascript的动态语言特性
2015/06/17 Javascript
jquery实现select下拉框美化特效代码分享
2015/08/18 Javascript
jquery中ajax跨域方法实例分析
2015/12/18 Javascript
jQuery simpleModal插件的使用介绍
2016/08/30 Javascript
在vue.js中抽出公共代码的方法示例
2017/06/08 Javascript
Vue 父子组件的数据传递、修改和更新方法
2018/03/01 Javascript
Node.js如何优雅的封装一个实用函数的npm包的方法
2019/04/29 Javascript
jquery 验证用户名是否重复代码实例
2019/05/14 jQuery
node将geojson转shp返回给前端的实现方法
2019/05/29 Javascript
Python中的map()函数和reduce()函数的用法
2015/04/27 Python
python中装饰器级连的使用方法示例
2017/09/29 Python
解决python3中解压zip文件是文件名乱码的问题
2018/03/22 Python
利用Python如何生成便签图片详解
2018/07/09 Python
Python 函数返回值的示例代码
2019/03/11 Python
详解python读取image
2019/04/03 Python
Python OpenCV实现测量图片物体宽度
2020/05/27 Python
美国性感女装网站:bebe
2017/03/04 全球购物
Reformation官网:美国女装品牌
2018/09/14 全球购物
法国二手MacBook销售网站:Okamac
2019/03/18 全球购物
HQhair美国/加拿大:英国化妆品、美容及美发产品商城
2019/04/15 全球购物
预备党员思想汇报范文
2014/01/11 职场文书
企业厂务公开实施方案
2014/03/26 职场文书
小学班主任评语大全
2014/04/23 职场文书
反四风个人对照检查材料
2014/09/26 职场文书
Windows11性能真的上涨35%? 桌面酷睿i9实测结果公开
2021/11/21 数码科技