angularjs 控制器、作用域、广播详解
一、控制器
首先列出几种我们平常使用控制器时的几种误区:
我们知道angualrJs中一个控制器时可以对应不同的视图模板的,但这种实现方式存在的问题是:
如果视图1和视图2根本没有任何逻辑关系,这样“控制器”的角色就会很尴尬,因为我们不可能把不同业务的数据模型都绑在同一个控制器中。
这种实现方式也存在一个问题是:如果控制器1和控制器2里面有2个方法是一模一样的怎么办?
<!doctype html> <html ng-app> <head> <meta charset="utf-8"> </head> <body> <div ng-controller="CommonController"> <div ng-controller="Controller1"> <p>{{greeting.text}},Angular</p> <button ng-click="test1()">test1</button> </div> <div ng-controller="Controller2"> <p>{{greeting.text}},Angular</p> <button ng-click="test2()">test2</button> <button ng-click="commonFn()">通用</button> </div> </div> </body> <script src="js/angular-1.3.0.js"></script> <script src="MVC3.js"></script> </html>
function CommonController($scope){ $scope.commonFn=function(){ alert("这里是通用功能!"); }; } function Controller1($scope) { $scope.greeting = { text: 'Hello1' }; $scope.test1=function(){ alert("test1"); }; } function Controller2($scope) { $scope.greeting = { text: 'Hello2' }; $scope.test2=function(){ alert("test2"); } }
虽然子级控制器可以继承父级控制器的作用域及方法,但是我们一般不要去这样做,因为很可能会造成作用域的混乱。
正确的方式应该是这样的:我们把公共的方法抽离出来,放在公共的服务当中去,需要的时候从公共的服务中调取就好了。
在使用控制器时要注意几点:
1.不要去复用controller,一个控制器一般只负责一小块视图;(一般控制器处理的都是业务逻辑,业务逻辑的复用性一般很小)
2.不要在controller中操作DOM,这不是控制器的职责;(因为在 controller里面操作DOM会导致浏览器页面的重绘,这种代价是昂贵的)
3.一般不要在控制器里面做数据过滤操作,ng有$filter服务;
一般来说,Controller是不会相互调用的,控制器之间的交互会通过广播事件进行!
二、作用域
angularJs的MVC是借助$scope来实现的!
先来看一段代码:
<!doctype html> <html ng-app> <head> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="Scope1.css" /> </head> <body> <div class="show-scope-demo"> <div ng-controller="GreetCtrl"> Hello {{name}}! </div> <div ng-controller="ListCtrl"> <ol> <li ng-repeat="name in names"> {{name}} from {{department}} </li> </ol> </div> </div> </body> <script src="js/angular-1.3.0.js"></script> <script src="Scope1.js"></script> </html>
function GreetCtrl($scope, $rootScope) { $scope.name = 'World'; $rootScope.department = 'Angular'; } function ListCtrl($scope) { $scope.names = ['Igor', 'Misko', 'Vojta']; }
上面是两个不同的控制器,尽管ListCtrl控制器里面没有department,但它依然可以访问到department上的变量值。
神奇的$scope
1.$scope是一个对象;
2.$scope是表达式的执行环境(或者叫做作用域)(它是视图和控制器之间的胶水);
3.$scope提供了一些工具方法$watch()/$apply();
(这个是实时检测对象属性变化的,在修改数据时会立刻更新$scope,当$scope发生变化时会立刻重新渲染视图);
(这两个方法虽然提供了监视数据模型变化的能力,将数据模型的变化在整个应用范围内进行通知,但一般我们不太会手动去调用$scope.$apply())
4.$scope是一个树形结构,与DOM标签平行;
5.子$scope会继承父$scope上的属性和方法;
6.每个angularJs应用只有一个$rootScope,一般位于ng-app上,$rootScope是所有$scope的最上层,
($rootScope也是angularJs中最接近全局作用域的对象,在$rootScope上附加太多业务逻辑并不是好主意,这与污染javaScript的全局作用域是一样的)
7.$scope也是实现双向数据绑定的基础;
8.可以用angular.element($0).scope()来进行调试;
9.$scope可以在控制器之间传播事件,可以向上$scope.$emit();也可以向下$scooe.$broadcast();
最后附一张$scope的生命周期图:
创建(创建一个作用域)——链接($scope对象会链接到视图中)——更新(脏值检查)——销毁(销毁作用域)
三、广播
3.1相关概念
通常作用域之间是不共享变量的,但作用域是有层次的,所以我们可以在作用域上通过广播来传递事件。
Angularjs中不同作用域之间可以通过组合使用$emit,$broadcast,,$on的事件广播机制来进行通信
$emit的作用是将事件从子级作用域传播至父级作用域,包括自己,直至根作用域。格式如下:$emit(eventName,args)
$broadcast的作用是将事件从父级作用域传播至子级作用域,包括自己。格式如下:$broadcast(eventName,args)
$on用于在作用域中监控从子级或父级作用域中传播的事件以及相应的数据。格式如下:$on(event,data)
上述说明中,eventName是需要广播的事件的名称,args传递的数据集合,$on 方法中的参数event是事件的相关对象,data是事件传播的数据。
3.2实例说明angularjs $emit $broadcast $on
的用法
<div ng-controller="ParentCtrl"> <div ng-controller="SelfCtrl"> <a ng-click="click()">click</a> <div ng-controller="ChildCtrl"></div> </div> <div ng-controller="BroCtrl"></div> </div>
var appControllers = angular.module('myApp', []) appControllers.controller('SelfCtrl', ['$scope','$rootScope', function($scope, $rootScope){ var admin1 = { 'name': 'father', 'age': 45 }; var admin2 = { 'name': 'Lucy', 'age': 25 }; $scope.click = function() { //事件的发送 //向子级控制器传递数据和事件,只有ChildCtrl能接受到广播,还有自己 $scope.$broadcast('to-child', admin2); //向父级控制器传递数据和事件,只有parentCtrl能接收到广播,还有自己 $scope.$emit('to-parent', admin1); //$rootScope发出的广播所有的作用域都可以接受到,可以用于同级之间进行广播 $rootScope.$broadcast('to-bro', '平级的数据'); } }])
appControllers.controller('ParentCtrl', ['$scope', '$rootScope', function($scope, $rootScope){ //事件的接受 $scope.$on('to-parent', function(event, data){ console.log(event); }); }])
appControllers.controller('ChildCtrl', ['$scope', '$rootScope', function($scope, $rootScope){ $scope.$on('to-child', function(event, data){ console.log(data); }); }])
appControllers.controller('BroCtrl', ['$scope', '$rootScope', function($scope, $rootScope){ //$scope和$rootScope都可以接受到事件 $scope.$on('to-bro', function(event, data){ console.log(data); }); $rootScope.$on('to-bro', function(event, data){ console.log(data); }); }]);
上一篇: react实例:理解dva构建项目的原理