AngularJS深入探讨scope,继承结构,事件系统和生命(2)

<body ng-app ng-init="name='World'"> <h1>Hello, {{name}}</h1> <div ng-controller="HelloCtrl"> Say hello to: <input type="text" ng-model="name"> <h2>Hello, {{name}}!</h2> </div> </body>

运行一下这段代码,就可以发现,这个 name 变量尽管仅仅是定义在了最顶级的作用域上,但在整个应用中都是可见的!这说明变量是从作用域链上继承下来的。换句话说,变量是在父级作用域上定义的,然后在子级作用域中访问的。

现在,我们一起来看看,如果在 <input> 中写点字会发生什么,运行结果你可能会感到吃惊,因为 HelloCtrl 控制器所初始化的作用域创建了一个新的变量,而不是直接去修改$rootScope 实例中的值。不过当我们认识到作用域也只不过是在彼此间进行了原型继承,也就不会觉得那么吃惊了。所有可以用在 JavaScript 对象上的原型继承的规则,都可以同等的用在 作用域 的原型链继承上去。毕竟 Scopes 作用域就是 JavaScript 对象嘛。

在子级作用域中去改变父级作用域上面的属性有几种方法。第一种,我们就直接通过 $parent 属性来引用父级作用域,但我们要看到,这是一个非常不可靠的解决方案。麻烦之处就在于,ng-model 指令所使用的表达式非常严重的依赖于整个DOM结构。比如就在 <input> 标签上面的哪里插入另一个 可创建作用域 的指令,那$parent 就会指向一个完全不同的作用域了。

就经验来讲,尽量避免使用 $parent 属性,因为它强制的把 AngularJS 表达式和你的模板所创建的 DOM 结构捆绑在了一起。这样一来,HTML结构的一个小小的改动,都可能会让整个应用崩溃。

另一个解决方案就是,不要直接把属性绑定到 作用域上,而是绑到一个对象上面,如下所示:

<body ng-app ng-init="thing = {name : 'World'}"> <h1>Hello, {{thing.name}}</h1> <div ng-controller="HelloCtrl"> Say hello to: <input type="text" ng-model="thing.name"> <h2>Hello, {{thing.name}}!</h2> </div> </body>

这个方案会更可靠,因为他并没有假设 DOM 树的结构是什么样子。

避免直接把数据绑定到 作用域的属性上。应优先选择把数据双向绑定到对象的属性上(然后再把对象挂到 scope 上)。就经验而言,在给 ng-model 指令的表达式中,你应该有一个点(例如, ng-model="thing.name")。

作用域层级和事件系统

层级关系中的作用域可以使用 event bus(一种事件系统)。AngularJS可以在作用域层级中传播具名的装备齐全的事件。事件可以从任何一个作用域中发出,然后向上($emit)和向下($broadcast)四处传播。

AngularJS核心服务和指令使用这种事件巴士来发出一些应用程序状态变化的重要事件。比如,我们可以监听$locationChangeSuccess 事件(由 $rootScope 实例发出),然后在任何 location(浏览器中就是URL)变化的时候都会得到通知,如下所示:

$scope.$on('$locationChangeSuccess', function(event, newUrl, oldUrl){ //react on the location change here //for example, update breadcrumbs based on the newUrl });

每一个作用域对象都会有这个 $on 方法,可以用来注册一个作用域事件的侦听器。这个函数所扮演的侦听器在被调用时会有一个 event 对象作为第一个参数。后面的参数会根据事件类型的不同与事件本身的配备一一对应。

类似于 DOM 事件,我们可以调用 event 对象的 preventDefault() 和 stopPropagation() 方法。stopPropagation() 方法将会阻止事件沿着作用域层级继续冒泡,并且只在事件向上层传播的时候($emit)才有效。

尽管 AngularJS 的事件系统是模仿了 DOM 的,但两个事件传播系统是完全独立的,没有任何共同之处。

虽然在作用域层级中传播事件对一些问题来说是一种非常优雅方案(特别是对全局的,异步的状态变化来说),但还是要适度使用。通常情况下,可以依靠双向数据绑定来得到一个比较干净的方案。在整个 AngularJS 框架中,一共只发出($emit)了三个事件($includeContentRequested,$includeContentLoaded,$viewContentLoaded)七个广播($broadcast)($locationChangeStart, $locationChangeSuccess, $routeUpdate, $routeChangeStart,$routeChangeSuccess, $routeChangeError, $destroy)。正如你所看到的,作用域事件使用的非常少,我们应该在发送自定义的事件之前认真的评估一下其他的可选方案(多数会是双向数据绑定)。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wzggdf.html