文档章节

学习 AngularJS (六) scope 续

刘军兴
 刘军兴
发布于 2015/12/10 13:57
字数 1913
阅读 89
收藏 4
点赞 0
评论 0

继续昨天 scope 未学完的.

作用域层级和继承

在作用域中定义的属性对子作用域是可见的, 只要子作用域没有定义同名属性.
根据我们前面查看 scope 的结构, 推测 angular 用 __proto__ 链形成继承结构, 故而一个 scope 能访问到
祖先 scope 的所有属性. 实验一下: 在 repeater 中访问 ctrler 中定义的 {{name}}, 实验通过.

书上确实说了, 此作用域继承 (inheritance) 和 js 中原型继承 (prototypical inheritance) 遵循同样的规则.

作用域层级中读写的区别

在 scope 中读操作 (read access) 是可以访问到祖先层级的属性/方法, 写操作 (write acess) 是写到当前
作用域. (这也是因为用 __proto__ 链的原因).

如果想确切地读取/写入父 scope, 则需要使用 $parent 属性.
   如双向: <input type='text' ng-model='$parent.name' >

此方案缺点也很明显, 这里假设 name 在 $parent 层级, 如果中间加入别的 scope 层级, 就会导致失效.

另一个解决方案, 将变量绑定 (binding) 为某对象的属性, 该对象本身在任何 scope不变并可访问到,
只变化该对象的属性. 实验一下:

<!-- 将变量 name 作为 user 对象属性. -->
<div ng-app ng-init="user = {name:'foo'}">
  <h1>Hello {{user.name}}!</h1>
  <div ng-controller='HelloCtrl'> <!-- 这里产生子 scope -->
    Say hello to: <input type='text' ng-model='user.name'>
    <h2>hello, {{user.name}}!</h2>
  </div>
</div>

现在, 在 input 框中输入内容, 可以看到 h1,h2 中内容都发生变化.
这个方案要好一点, 它没有对 DOM 树做不必要的假设.

作用域层级与事件系统

AngularJS 提供命名事件 (named events) 在任何作用域开始分发 (dispatch), 向上分发称为 $emit, 向下广播
为 $broadcast. 

AngularJS 核心服务  (services) 与指令 (directives) 使用此机制发送信号, 通知应用状态的重要变化.
如当 url 变化时, 根作用域 $rootScope 广播 $locationChangeSuccess 事件, 子 scope 可通过监听
获得通知:
   

$scope.$on('$locationChangeSuccess', function (... event args) {
  // ...事件处理代码... 
});

模仿 DOM 事件, 作用域事件 (scope-event) 对象也有 preventDefault(), stopPropagation() 等方法.

书上说, AngularJS (只)有 3 种 emit 事件, 7 种 broadcast 事件, 要谨慎使用. 大部分情况下, 最好用
数据双向绑定. (猜测: 可能事件传输成本比较高, 或者容易递归死循环?)

作用域生命周期

作用域提供了独立的命名空间, 避免变量的名称冲突. 如果不需要作用域了, 可以销毁它(垃圾回收掉).
手工调用 Scope 类的 $new(), $destroy() 可手工创建/销毁作用域.

 

======

以上基本了解了作用域 (scope) 的概念. 让我们继续看 kityminder-editor 部分的源码.

前篇我们看到 kityminder-editor directive, 继续深入下去, 找到它包含的 undo-redo directive, 让我们单独
拆离它出来研究:

<div ng-app='kityminderDemo' ng-controller='MainController'>
  <undo-redo editor="editor" ></undo-redo>
  <!-- 原来是下面的这个 directives, 我们先注释掉 -->
  <kityminder-editor ... />
</div>

此时界面上会出现 undo, redo 两个按钮, 对应 HTML 是根据 undo-redo 指令的模板文件生成的, 为学得细一点,
摘要如下:

<!-- 最外层的 ng-app, 对应的 scope.$id = 1 (后面为测试, 结构略有调整) -->
<div ng-controller="MainController" ng-app="kityminderDemo" class="ng-scope">
  <!-- 由 undo-redo 指令展开的模板, 对应的 scope.$id=2 -->
  <div class="km-btn-group do-group ng-isolate-scope" editor="editor">
    <!-- 通过 css 设定显示为按钮样子, css 细节略. -->
    <div title="撤销 (Ctrl + Z)" ng-click="editor.history.undo();" ng-disabled="editor.history.hasUndo() == false" class="km-btn-item undo">
        <i class="km-btn-icon"></i>
    </div>
    <div title="重做 (Ctrl + Y)" ng-click="editor.history.redo()" ng-disabled="editor.history.hasRedo() == false" class="km-btn-item redo">
        <i class="km-btn-icon"></i>
    </div>
  </div>
</div>

这里用各种 css class 产生界面效果的我们略去, 主要关心:
   问题1: undo-redo 元素上的 editor 属性是什么意思; 以及
   问题2: div.undo 按钮上 ng-click, ng-disabled 是什么意思.

为此我们 hack 进入 undoRedo.directive.js 中加点调试代码 console.log() ... :

angular.module('kityminderEditor')
  .directive('undoRedo', function() { // 注册 undo-redo 指令
    return {
      restrict: 'E',  // 此指令只能作为元素使用.
      templateUrl: 'ui/directive/undoRedo/undoRedo.html', // 模板路径
      scope: {        // 创建一个独立的/隔离的 scope
        editor: '='   // 等价于 '=editor', 映射父 scope 的 editor 属性.
      },
      replace: true,  // 使用模板内容替换掉此指令内容, 可设置为 false 实验下.
      link: function($scope) {  // 连接 scope & directive
        // 为方便调试, 我们在这里输出此 directive 所在 scope 等信息.
        console.log('undo-redo link(), $scope is: ', $scope);
      }
    }
  });

这里大概能部分解答我的问题1, 即 editor 是什么意思, 现在知道它 (editor) 是从父级 scope 映射过来的, 那我们
现在研究一下父级 scope 从哪里弄来 editor 的.

由于原来 undo-redo 指令是含于 kityminder-editor 指令里面的, 而后者又被含于... 看代码:

<body ng-app='demo'>   <!-- 第一层 scope, 也是 $rootScope -->
  <div ng-controller='MainController'>   <!-- 第二层 scope -->
    <kityminder-editor />   <!-- 第三层 scope, 展开有更多 scope -->
    <!-- 这里引用 kityminder-editor 指令原来还带 on-init 属性, 其不影响提供 editor,minder 属性 -->
  </div>
</body>

然后看 Chrome 中 Scope 图:

 

经过查看, 是 scope.$id=3 那一层提供的 editor, minder 对象. 其即对应 kityminder-editor 指令.
提供这两个对象和 on-init 也无关, HTML 中注释中说明了实验该情况的说明. 所以再细看该指令:

angular.module('km...')
  .directive('kityminderEditor', ... function() {
    return {
      // 各种复杂的暂时看不懂的先略去.
      link: function(scope, ...) {
        // 在某个嵌套又嵌套的函数中有:
        var KMEditor = seajs.require('editor');  // 引入 KMEditor 类.
        var editor = new KMEditor();  // 实例化一个 editor.
        
        scope.editor = editor;
        scope.minder = editor.minder;
        // 其它暂时不明白的略去, 总之这里为 scope 提供了域数据.
      }
    };
  });

为了验证, 实验在设置 scope.x=y 处加入一个 console.log(), 实验结果能看到该信息显示于 console.
以后有空再研究这里使用 seajs.require() 带来的回调/时序问题吧. 以及一些别的相关连接调用问题.

现在已知这里产生了 editor, minder 属性, 那为了仅测试 undo-redo, 我们不妨弄一个假的 controller,
提供测试用 editor, minder 属性和方法. 让我们实验吧:

<!-- 外面 body 及其 ng-app 略, 我们将 controller 换为 Fake 版的 -->
<div ng-controller='FakeController'>
  <undo-redo />  <!-- 这里我们连属性 editor='editor' 我们也去掉, 看看到底如何 -->
</div>
<script>
// 注册我们的 FakeController, 好为 scope 提供假的 editor, minder.
angular.module('demo')
  .controller('FakeController', function($scope) { // 注册此假 controller
    $scope.editor = 'fake-editor';  // 一会代换这些字符串为测试对象.
    $scope.minder = 'fake-minder';
  });
</script>

 

现在在元素 <div ng-controller> 上查看 scope, 果然有 editor, minder 属性了. (图就略了)

这里还实验出一个东西, 如果在 <undo-redo > 里面没有 editor='editor' , 则在 undo-redo 的 scope
中也就不会映射 parent-scope 的 editor 进入到自己. (似乎用起来麻烦点...?) 

现在我们已经知道 undo-redo 中的 editor 从哪里来了, 让我们将其代换为一个测试对象:

// 在 FakeController 中添加/改写点测试代码:
var test_minder = {
  str: 'This is test-minder.'
};
var test_editor = {
  minder: test_minder,
  history: {
    undo: function() {
      console.log('您点击了 undo 按钮.');
    },
    redo: function() {
      console.log('您点击了 redo 按钮.');
    }
  },
};

$scope.editor = test_editor;
$scope.minder = test_minder;

 

这时, 让我们再点击 undo, redo 按钮, 此时在 console 上就能看到我们的 log 输出了! 此实验通过.
同样, 提供 hasUndo(), hasRedo() 方法于 editor.history 对象上, 也能看到 console 上对应输出 (略).

这样, 我们就基本明白了 undo-redo 指令的一般实现, 如何引用到对象, 处理 ng-click 事件等.

在这里, undo-redo 使用了 isolate-scope 机制, 我们之前看过但没有实例理解, 现在这就是一个实例.
通过 isolate scope 只引入了 editor 属性从 parent scope.
如果此 directive 不考虑复用因素, 其实不使用 isolate-scope, 甚至不创建自己的 scope 会更简单一点,
连元素上 editor='editor' 的似乎 ``自己=自己'' 的多余写法都不用写了.

休息一下, 以后接着学.

© 著作权归作者所有

共有 人打赏支持
刘军兴
粉丝 54
博文 150
码字总数 226172
作品 0
昌平
【前端】—聊聊我认识的Angular

前言 最近接触的项目前端用到了Angular框架,之前略有耳闻,从vue换到Angular,感觉东西差不多,还是要系统学习的,先来了解下。 正文 1、Angular 的发展 AngularJS 是一款来自Google的前端J...

zt15732625878 ⋅ 05/19 ⋅ 0

再谈angularJS数据绑定机制及背后原理—angularJS常见问题总结

Angular 的数据绑定采用什么机制,详述原理? 脏检查机制。阐释脏检查机制,必须先了解如下问题。 单向绑定(ng-bind) 和 双向绑定(ng-model) 的区别? ng-bind 单向数据绑定($scope ->...

634117608 ⋅ 04/19 ⋅ 0

[Angular Material完全攻略] Day 01 - 开始 & 简介

转载 从Angular第2版正式release后,根据全球最大工程师讨论区StackOverflow的统计,从2016开始的Angular讨论度就不断窜升,甚至超越了React,直到了2017年,甚至摆脱了前一代Angularjs的阴影...

readilen ⋅ 05/21 ⋅ 0

Angular 6.0正式版发布,都有哪些新功能

点击关注异步图书,置顶公众号 每天与你分享IT好书 技术干货 职场知识 在Angular 5发布半年之后,Angular 6在昨天正式发布,那么在这个版本有哪些新功能呢?新版本重点关注工具链以及工具链在...

异步社区 ⋅ 05/08 ⋅ 0

Angular 6正式版发布,都有哪些新功能

在Angular 5发布半年之后,Angular 6在昨天正式发布,那么在这个版本有哪些新功能呢?新版本重点关注工具链以及工具链在 Angular 中的运行速度问题。除此之外,这次更新还包括框架包(@angu...

code_xzh ⋅ 05/05 ⋅ 0

[Angular Material完全攻略] Day 02 - 环境设定 & 安装 & Hello World

今天我们将开始正式迈入Angular Material的世界,要学习使用Angular Material打造高品质及高质感的网页,当然要从安装Angular Material套件开始,本篇文章就来介绍基本的Angular Material安装...

readilen ⋅ 05/21 ⋅ 0

初学angular 看到网上有angular js 也有angular2 ,到angular官网发现最近版本是6了,那么现在大家说的angular js到底是什么啊?

初学angular 看到网上有angular js 也有angular2 ,到angular官网发现最近版本是6了,那么现在大家说的angular js到底是什么啊? angular2和现在官网的angular6 就是 angular js 只是版本不同...

Jordan裔 ⋅ 05/19 ⋅ 0

AngularJS 的 Material Design 风格框架 - AngularJS Material

AngularJS Material 是 AngularJS 框架的谷歌 Material Design 标准的实现。AngularJS Material 包含一组丰富的、可重用、经过充分测试并可访问的 UI 组件。 针对 Angular 2 或更高版本的实现...

匿名 ⋅ 05/15 ⋅ 0

Angular 6 服务端渲染之 udao 终章

先介绍下小朋友 udao,首先是一个开源项目,代码足够简单,其次是跟随 Angular 大小版本一起成长的项目,会定期更新所有依赖包以及兼容最新版本的写法 Github 地址也贴出来好多次了:github....

orangexc ⋅ 05/10 ⋅ 0

想开发Angular项目,但是没有开发环境?使用Docker So Easy!

不管是的开发还是学习Angular,环境是一个很大的问题,大家要在自己电脑上搭建一个Angular的开发测试环境还是非常麻烦的,请大家自行百度“搭建Angular开发环境”。OMG,要安装一堆东西。 但...

麦兜搞IT ⋅ 05/21 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Java Web如何操作Cookie的添加修改和删除

创建Cookie对象 Cookie cookie = new Cookie("id", "1"); 修改Cookie值 cookie.setValue("2"); 设置Cookie有效期和删除Cookie cookie.setMaxAge(24*60*60); // Cookie有效时间 co......

二营长意大利炮 ⋅ 今天 ⋅ 0

【每天一个JQuery特效】淡入淡出显示或隐藏窗口

我是JQuery新手爱好者,有时间就练练代码,防止手生,争取每天一个JQuery练习,在这个博客记录下学习的笔记。 本特效主要采用fadeIn()和fadeOut()方法显示淡入淡出的显示效果显示或隐藏元...

Rhymo-Wu ⋅ 今天 ⋅ 0

Spring JDBC使用方法

普通实现: 1、创建数据表customer。 可以使用任何数据库实现,在项目中要引入相应数据库驱动包并配置相应数据库连接。 2、创建Customer pojo。 Customer类的属性对应数据库的属性,除了为每...

霍淇滨 ⋅ 今天 ⋅ 0

Contos 7 安装Jenkins

Jenkins是一款能提高效率的软件,它能帮你把软件开发过程形成工作流,典型的工作流包括以下几个步骤 开发 提交 编译 测试 发布 有了Jenkins的帮助,在这5步中,除了第1步,后续的4步都是自动...

欧虞山 ⋅ 今天 ⋅ 0

revel

revel install go get github.com/revel/revelgo get github.com/revel/cmd create new app revel new git.oschina.net/zdglf/myapp run app revel run git.oschina.net/zdglf/myapp ot......

zdglf ⋅ 今天 ⋅ 0

49. Group Anagrams - LeetCode

Question 49. Group Anagrams Solution 思路:维护一个map,key是输入数组中的字符串(根据字符排好序) Java实现: public List<List<String>> groupAnagrams(String[] strs) { Map<Strin......

yysue ⋅ 今天 ⋅ 0

spring Email

使用spring发Email其实就是使用spring自己封装携带的一个javamail.JavaMailSenderImpl类而已。这个类可以当一个普通的java对象来使用,也可以通过把它配置变成spring Bean的方式然后注入使用...

BobwithB ⋅ 今天 ⋅ 0

spark 整理的一些知识

Spark 知识点 请描述spark RDD原理与特征? RDD全称是resilient distributed dataset(具有弹性的分布式数据集)。一个RDD仅仅是一个分布式的元素集合。在Spark中,所有工作都表示为创建新的...

tuoleisi77 ⋅ 今天 ⋅ 0

思考

时间一天天过感觉自己有在成长吗?最怕的是时光匆匆而过,自己没有收获!下面总结下最近自己的思考。 认识自己 认识另一个自己,人们常说要虚心听取别人意见和建议。然而人往往是很难做到的,...

hello_hp ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部