AngularJS 事件

AngularJS 事件系统并不与浏览器的事件系统相通,这意味着,我们只能在作用域上监听 AngularJS 事件而不是 DOM 事件。由于作用域是有层次的(既是继承关系),所以可以在作用域链上传递事件。在传递事件前,需要先了解触发事件的作用域和处理事件的作用域的关系,这样我们才可以判断事件是向上传递还是向下广播。

a. 事件如何传播

什么时候需要向上传递事件?当处理事件的作用域为触发事件的作用域的父级作用域时,就需要将事件向上传递。同理,当处理事件的作用域为触发事件的作用域的子级作用域时,就需要将事件向下传递。
1. 使用$emit向上传递事件
  使用$emit将事件沿着作用域链向上传递,即从子作用域到父作用域。

语法:
scope.$emit(name, args)

参数:

name
  类型:String,要触发事件的名称。

args
  参数集合,可以是一个也可以是多个,作为对象传递到事件监听者中。

eg-1:

1
$scope.$emit('just-test-emit', ['first'], ['second']);

  • eg-1中,just-test-emit为事件名称,而[first][second]为传递到事件监听者中的参数。
  • $emit()事件函数调用后,事件会从子作用域冒泡到父作用域。在触发事件的作用域之上的所有作用域都会收到这个事件的通知。

2. 使用$broadcast向下广播事件
  $broadcast$emit用法很类似,只是$broadcast是将事件沿着作用域向下传递,即从父作用域到子作用域。

语法:
scope.$broadcast(name, args)

参数:

name
  类型:String,要触发事件的名称。

args
  参数集合,可以是一个也可以是多个,作为对象传递到事件监听者中。

eg-2:

1
$scope.$broadcast('just-test-broadcast', ['first'], ['second']);

  • $broadcast()事件函数调用后,每个在子作用域注册了事件的监听者都会收到这个事件通知。

3. 使用$on监听事件
  使用$on根据事件的名称,注册一个监听者。同时$on函数会返回一个反注册函数,我们可以通过调用这个反注册函数取消事件监听者。

语法:
scope.$on(name, listener)

参数:

name
  类型:String,要监听的事件名称。

listener
  类型:Function,事件监听者。
  函数的格式:function(event, args...),AngularJS会把event对象作为第一个参数传给正在监听事件的监听者(不管是自定义的事件还是AngularJS内置的事件)。args为触发事件时传递给事件监听者的参数集合。event(事件)对象属性:
   event.targetScope
    类型:Scope,表示事件触发所在的那个作用域。
   event.currentScope
    类型:Scope,当前事件处理的作用域。
   event.name
    类型:String,事件的名称。
   event.stopPropagation
    类型:Function,调用后会取消事件的冒泡,也就是取消通过$emit触发的事件进一步在作用域链上向上传递。
   event.preventDefault
    类型:Function,调用后会将defaultPrevented设置为true,同时告诉子作用域无需处理这个事件,但是不能阻止事件的传播。
   event.defaultPrevented
    类型:Boolean

b. 事件相关的核心服务

  • 核心系统的$emitted事件
      下面的事件从指令向上传递到包含指令调用的作用域。

  1. $includeContentRequested
   这个事件从调用ngInclude的作用域向上传递。每次ngInclude的内容被请求时,都会被触发。
  2. $includeContentLoaded
   当ngInclude的内容重新加载时,将会从ngInclude指令上触发。
  3. $includeContentError
   当HTTP请求模板错误时触发。
  4. $viewContentLoaded
   每当ngView内容被重新加载时,将会从当前ngView作用域向上传递事件。

  • 核心系统的$broadcast事件
      1. $locationChangeStart
       当 AngularJS 从 $location 服务(通过$location.path()、$location.search()等)对浏览器的地址作更新时,会触发$locationChangeStart事件。
      2. $locationChangeSuccess
       当且仅当浏览器的地址变更成功后,同时又没有阻止$locationChangeStart事件的情况下,$locationChangeSuccess事件会从$rootScope作用域向下广播出来。
      3. $routeChangeStart
       在路由变更发生之前,$routeChangeStart事件会从$rootScope作用域广播出来。也就是在路由服务开始解析路由变更所需的所有依赖项时。
      4. $routeChangeSuccess
       在所有路由依赖项跟着$routeChangeStart被解析后,$routeChangeSuccess事件就会从$rootScope作用域上广播出来。ngView指令使用$routeChangeSuccess事件来获悉何时实例化控制器并渲染视图。
      5. $routeChangeError
       如果路由对象上任意的resolve属性被拒绝,$routeChangeError就会被触发。
      6. $routeUpdate
       如果$routeProvider上的reloadOnSearch属性被设置成false,并且使用了控制器的同一实例,$routeUpdate就会被触发,同时会从作用域$rootScope向下传递。
      7. $destroy
       在作用域被销毁之前,$destroy事件会在作用域上广播,这样子作用域在父作用域被真正移除之前可以先清理自身。
       例如,如果在控制器中有一个正在运行的$interval,我们不希望在包含它的控制器已经不存在的情况下,它还继续触发,就可以在作用域销毁前,先取消掉$interval
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var autoPolling = $interval(function() {
initData();
}, 3000);

$scope.stopAutoPolling = function() {
if (angular.isDefined(autoPolling)) {
$interval.cancel(autoPolling);
autoPolling = undefined;
}
};

$scope.$on('$destroy', function() {
$scope.stopAutoPolling();
});

参考

  • 《Angular JS权威指南》