Angular按照发布/订阅模式设计了其事件系统,使用时需要“发布”事件,并在适当的位置“订阅”或“退订”事件,就像邮箱里面大量的订阅邮件一样,当我们不需要时就可以将其退订了。具体到开发中,对应着$scope和$rootScope的$emit$broadcast$on方法。本文介绍Angular的事件机制,包括$scope和$rootScope处理事件上的异同,$broadcast、$emit和$on的使用方式及他们区别等内容。

$scope与$scope之间的关系,$scope与$rootScope之间的关系

要理解Angular的事件机制,首先需要了解$scope$scope之间的关系以及$scope$rootScope之间的关系。$rootScope是唯一真神,是万域起源,是所有$scope的最终祖先。而$scope$scope之间可能的关系包括父子关系和兄弟关系。还记得controller之间的关系吗,Angular为每个controller分配一个独立的$scope,controller之间的关系也对应着$scope之间的关系:

<div ng-controller="ParentCtrl as parent">
{{ parent.data }}
<div ng-controller="SiblingOneCtrl as sib1">
{{ sib1.data }}
</div>
<div ng-controller="SiblingTwoCtrl as sib2">
{{ sib2.data }}
</div>
</div>

发布、订阅、退订

$broadcast$emit用于发布事件,他们将事件名称和事件内容发布出去,就像是高考榜单一样,事件名称相当于考生的名字,而事件内容相当于考生的成绩等信息:

$scope.$broadcast('EVENT_NAME', 'Data to send');
$scope.$emit('EVENT_NAME', 'Data to send');

$on用于订阅事件,事件名称是订阅的唯一标识,每个考生看榜单时都要寻找自己的名字,然后根据自己的成绩等信息决定下一步应该报考什么学校:

$scope.$on('EVENT_NAME', function(event, args) {
// balabala
});

Angular的退订事件有些奇怪,并没有类似于其他语言的$off方法,所以不要想当然的按照如下方式进行事件的退订操作:

// 不要这样做
$scope.$off('EVENT_NAME');

事实上,Angular的事件退订方法隐藏在事件订阅里面:使用$on订阅事件时会返回一个函数,而此函数就是用来退订事件的方法,就像是考生看到了自己的成绩后禀告父母大人,“商量着”选取学校填报志愿,而此志愿单就是结束整个高考榜单的结束:

// 订阅事件返回用于退订事件的函数
var deregister = $scope.$on('EVENT_NAME', function(event, args) {
// balabala
}); // 退订事件
deregister();

$broadcast相当于战斗机轰炸,$emit相当于射箭

$broadcast$emit都用于发布事件,但从名字就可以看出他们的不同点:$broadcast是自上而下的广播,所有能听到的都可以对其进行反应。而$emit是自下而上的射箭,只有在箭矢的轨迹上才能对其做出反应。

具体到Angular上,即从一个$scope上通过$broadcast发布的事件,他的所有后代$scope都可以对此事件做出响应:

// 父$scope通过$broadcast发布事件
app.controller('ParentCtrl', ['$scope', function($scope) {
$scope.$broadcast("parent", 'Data to Send');
}])
//所有子$scope都可以通过$on订阅事件
.controller('SiblingOneCtrl', ['$scope', function($scope) {
$scope.$on("parent", function(event, 'Data to Send') {
// balabala
});
}])
.controller('SiblingTwoCtrl', ['$scope', function($scope) {
$scope.$on("parent", function(event, 'Data to Send') {
// balabala
});
}]);

而通过$emit发布的事件,只有他的祖先$scope可以做出响应,并且其中任一祖先都可以将此事件终结掉,不让其继续传播:

// 子$scope通过$emit发布事件
app.controller('SiblingOneCtrl', ['$scope', function($scope) {
$scope.$emit("sib1", 'Data to Send');
}])
// 父$scope通过$on订阅事件
.controller('ParentCtrl', ['$scope', function($scope) {
$scope.$on("sib1", function(event, 'Data to Send') {
// balabala
});
}])
// 其兄弟$scope对其$emit的事件一无所知,所以不能订阅其事件
.controller('SiblingTwoCtrl', ['$scope', function($scope) {
// 不要这样做
$scope.$on("sib1", function(event, 'Data to Send') {
// balabala
});
}]);

$emit发布事件的响应道路上,其任一祖先如果感觉不再需要此事件了,就可以通过如下方式终结此事件:

app.controller('ParentCtrl', ['$scope', function($scope) {
$scope.$on("sib1", function(event, 'Data to Send') {
// balabala
event.stopPropagation(); // 终止事件继续“冒泡”
});
}])

$rootScope的$broadcast和$emit

上面说过$rootScope是所有$scope的最终祖先,所以通过$rootScope$broadcast发布的事件可以被所有$scope接收到,包括$rootScope

app.controller('SomeCtrl', ['$rootScope', function($rootScope) {
$rootScope.$broadcast("rootEvent", 'Data to Send'); // $rootScope也可以通过$on订阅从$rootScope.$broadcast发布的事件
$rootScope.$on("rootEvent", function(event, 'Data to Send') {
// balabala
});
}])
// 所有$scope都能够通过$on订阅从$rootScope.$broadcast发布的事件
.controller('ParentCtrl', ['$scope', function($scope) {
$scope.$on("rootEvent", function(event, 'Data to Send') {
// balabala
});
}])
.controller('SiblingOneCtrl', ['$scope', function($scope) {
$scope.$on("rootEvent", function(event, 'Data to Send') {
// balabala
});
}])

$rootScope$emit就有些怪异了,按照上面的描述,$rootScope是没有祖先的,所以我们可能会想到其$emit会没有任何作用,但事实并不如此:$rootScope.$emit发布的事件,只能通过$rootScope.$on订阅,而其他$scope对此一无所知:

app.controller('SomeCtrl', ['$rootScope', function($rootScope) {
$rootScope.$emit("rootEvent", 'Data to Send'); // 只有$rootScope可以通过$on订阅从$rootScope.$emit发布的事件
$rootScope.$on("rootEvent", function(event, 'Data to Send') {
// balabala
});
}])
// $scope不能够通过$on订阅从$rootScope.$emit发布的事件
.controller('ParentCtrl', ['$scope', function($scope) {
// 不要这样做
$scope.$on("rootEvent", function(event, 'Data to Send') {
// balabala
});
}]);

退订$rootScope上的订阅事件

当使用$rootScope.$on订阅事件时,需要手动退订事件,一般在其所处$scope$destory事件中退订:

app.controller('SomeCtrl', ['$rootScope', '$scope', function($rootScope, $scope) {
var deregister = $rootScope.$on("rootEvent", function(event, 'Data to Send') {
// balabala
}); $scope.$on('$destory', function() {
deregister(); // 退订事件
});
}])

那通过$scope.$on订阅的事件呢?一般不需要手动退订,因为Angular会帮我们退订,但是如果需要自己控制何时退订事件,也可以通过上述方式进行退订。

事件命名的建议

在开发中,对于变量的命名、函数的命名、文件的命名都有一定的规范,既要保证可读性,也需要保证无混淆性。在Angular的事件机制中,因为事件可能会跨函数,甚至可能跨文件,所以对于事件名一定要保证唯一性,所以建议事件名都加上特定的前缀,以便区分。如下几个例子:

$scope.$emit('trash:delete', data);
$scope.$on('trash:delete', function (event, data) {...}); $scope.$broadcast('trash:clear', data);
$scope.$on('trash:clear', function (event, data) {...});

Angular $scope和$rootScope事件机制之$emit、$broadcast和$on的更多相关文章

  1. ng $scope与$rootScope的关系

    $scope与$rootScope的关系:①不同的控制器之间 是无法直接共享数据②$scope是$rootScope的子作用域对象$scope的id是随着控制器的加载顺序依次递增,$rootScope ...

  2. nodeJS中的事件机制

    events模块是node的核心模块,几乎所有常用的node模块都继承了events模块,比如http.fs等.本文将详细介绍nodeJS中的事件机制 EventEmitter 多数 Node.js ...

  3. 【移动端兼容问题研究】javascript事件机制详解(涉及移动兼容)

    前言 这篇博客有点长,如果你是高手请您读一读,能对其中的一些误点提出来,以免我误人子弟,并且帮助我提高 如果你是javascript菜鸟,建议您好好读一读,真的理解下来会有不一样的收获 在下才疏学浅, ...

  4. tkinter事件机制

    一.tkinter.Event tkinter的事件机制跟js是一样的,也是只有一个Event类,这个类包罗万象,集成了键盘事件,鼠标事件,包含各种参数. 不像java swing那种强类型事件,sw ...

  5. [解惑]JavaScript事件机制

    群里童鞋问到关于事件传播的一个问题:“事件捕获的时候,阻止冒泡,事件到达目标之后,还会冒泡吗?”. 初学 JS 的童鞋经常会有诸多疑问,我在很多 QQ 群也混了好几年了,耳濡目染也也收获了不少,以后会 ...

  6. Atitit  数据库的事件机制--触发器与定时任务attilax总结

    Atitit  数据库的事件机制--触发器与定时任务attilax总结 1.1. 事件机制的图谱1 2. 触发器的类型2 3. 实现原理 After触发器 Vs Instead Of触发器2 3.1. ...

  7. 深入浅出iOS事件机制

    原文地址: http://zhoon.github.io/ios/2015/04/12/ios-event.html 本文章将讲解有关iOS事件的传递机制,如有错误或者不同的见解,欢迎留言指出. iO ...

  8. Java 事件机制

    java事件机制包括三个部分:事件.事件监听器.事件源. 1.事件.一般继承自java.util.EventObject类,封装了事件源对象及跟事件相关的信息,用于listener的相应的方法之中,作 ...

  9. JavaScript 详说事件机制之冒泡、捕获、传播、委托

    DOM事件流(event  flow )存在三个阶段:事件捕获阶段.处于目标阶段.事件冒泡阶段. 事件捕获(event  capturing):通俗的理解就是,当鼠标点击或者触发dom事件时,浏览器会 ...

随机推荐

  1. SQL.集合转字符串

    场景:有个表,里面有NUM1,NUM2,NUM3,NUM4,我们打算把所有数据拼接成一个值. 在.net中使用的方法应该是两层for循环拼接起来,下面来介绍下sql中的方式 第一种方法,动态sql d ...

  2. curl开源库编译

    环境:Win7+VS2013 1.下载curl的zip包并解压,本文下载的是curl-7.39.0.zip,下载地址:http://curl.haxx.se/download.html 2.打开vs2 ...

  3. visualssh 是一个可视化的ssh客户端

    这两个周末写了visualssh,一个可视化的ssh客户端.里面集成了putty的功能,通过ssh协议与Linux server通讯. 以后可以针对不同的应用编写相关插件,便于管理Linux服务器上的 ...

  4. Android环境虚拟WINDOWS系统

    参考文档:http://bbs.anzhi.com/thread-5120526-1-1.html 我们知道安卓手机是arm平台,windows是x86平台,指令集完全不同,但在这里要教给大家的是靠软 ...

  5. jsp内置对象浅谈

    jsp内置对象浅谈 | 浏览:1184 | 更新:2013-12-11 16:01 JSP内置对象:我们在使用JSP进行页面编程时可以直接使用而不需自己创建的一些Web容器已为用户创建好的JSP内置对 ...

  6. setInterval和setTimeout

    setTimeout和setInterval的基本用法我们不谈,无非是1.指定延迟后调用函数,2.以指定周期调用函数 让我们想象一个意外情况,比如说下面的setInterval setInterval ...

  7. 配置使用EF6.0常见的一些问题及解决方案

    前言 最近做了个winform小项目,为方便快速开发,后台框架使用了ef6.0+sqlserver2008架构,遇到各种问题,真是伤脑筋.现将遇到问题和解决方案写下来,方便查阅 提示未注册,找不到驱动 ...

  8. sumoselect插件

    由于项目需要,研究了下sumoselect插件,接下来简单介绍下sumoselect. 在百度上搜索“sumoselect.js”,查到的网页基本上都有对sumoselect的基本介绍,如下: 简单介 ...

  9. Oracle优化总结

    本文主要从大型数据库ORACLE环境四个不同级别的调整分析入手,分析ORACLE的系统结构和工作机理,从九个不同方面较全面地总结了ORACLE数据库的优化调整方案.关键词 ORACLE数据库 环境调整 ...

  10. DOM example

    一:DOM简介: 文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标志语言的标准编程接口.在网页上,组织页面(或文档)的对象被组织在一个树形结构中, ...