angular作为在SPA开发中一个很强大的框架,数据操作是一大特点,而每个数据都有他们自己相应的作用域(即在自己的控制器内),那么,如何实现控制器之间的数据交互呢?归纳总结出来有如下几种:
控制器之间是父子关系——继承方式
这样的交互方式是由于作用域的继承是基于js的原型继承方式的,所以需要分成两种情况,当作用域上的值(即你需要操作的数据)是基本类型的时候,修改父级作用域上面的值的同时会影响到子级作用域,但是修改子级作用域的值却不会改变父级作用域。另外一种情况是:作用域上的值是对象,这样无论是修改父级作用域还是子级作用域,两个都会改变!具体demo,可以查看我的上一篇文章。
基于广播机制的方式
其实利用父子的继承方式已经能处理angular大部分的数据交互了,不过,当我们需要在兄弟节点进行数据交互的话,父子继承方式就显得那么有心无力了,那么这个时候就需要用到了我们的广播机制进行数据交互,具体我们来看下面的一个demo:
先来看下控制器中的代码:
- var routeApp = angular.module('routeApp', []);
- routeApp.controller('parentCtrl',['$scope',function($scope){
- $scope.parentname = "This is parent init name meichao";
- $scope.$on('firstChild', function(event,data){
- $scope.parentname = "This is firstChild change name";
- $scope.$broadcast('parent_to_secondCtrl', data);
- });
- $scope.$on('secondChild', function(event,data){
- $scope.parentname = "This is secondChild change name";
- $scope.$broadcast('parent_to_firstCtrl', data);
- });
- }]);
- routeApp.controller('firstChildCtrl',['$scope',function($scope){
- $scope.firstChidname = "This is firstChildCtrl init name";
- $scope.firstClick = function(){
- $scope.$emit('firstChild', $scope.firstChidname);
- }
- $scope.$on('parent_to_firstCtrl', function(event,data){
- $scope.firstChidname = data;
- });
- }]);
- routeApp.controller('secondChildCtrl',['$scope',function($scope){
- $scope.secondChidname = "This is secondChildCtrl init name";
- $scope.secondClick = function(){
- $scope.$emit('secondChild', $scope.secondChidname);
- }
- $scope.$on('parent_to_secondCtrl', function(event,data){
- $scope.secondChidname = data;
- });
- }]);
再来看下我们的html中的代码:
- <div ng-controller="parentCtrl">
- <div>{{parentname}}</div>
- <br>
- <div ng-controller="firstChildCtrl">
- <div>这里是firstChildCtrl中的可能会被改变的值:{{firstChidname}}</div>
- <br>
- <input type="text" ng-model="firstChidname">
- <div class="btn" ng-click="firstClick()">点我first</div>
- </div>
- <div ng-controller="secondChildCtrl">
- <div>这里是secondChildCtrl中的可能会被改变的值:{{secondChidname}}</div>
- <br>
- <input type="text" ng-model="secondChidname">
- <div class="btn" ng-click="secondClick()">点我second</div>
- </div>
- </div>
我们一共写了有3个控制器,一个父级,两个子级(子级控制器互为兄弟),利用广播机制来实现firstChildCtrl控制器和secondChildCtrl控制器之间的数据交互,各控制器内都有一个函数用来触发$emit事件(像父级传送数据),然后在parentCtrl控制器中用$on来接收数据,进而像另外一个子控制器进行$broadcast广播,从而改变另外一个自控制器中的数据,以达到兄弟控制器之间的数据交互。
具体效果可以自己写测试用例。
指令中的控制器与父级控制器之间的交互
当我们自写指令的时候,也会有与父级控制器之间进行数据交互,来看一个demo:
- <div ng-controller="parentCtrl">
- <div>{{parentname}}</div>
- <test/>
- </div>
- <script src="//cdn.bootcss.com/angular.js/1.3.9/angular.min.js"></script>
- <script>
- var routeApp = angular.module('routeApp', []);
- routeApp.controller('parentCtrl',['$scope',function($scope){
- $scope.parentname = "这里是父级控制器里面的内容";
- }]);
- routeApp.directive('test', function(){
- return {
- scope:true,
- template: '<div><div>{{parentname}}</div><input type="text" ng-model="parentname" /></div>',
- link: function($scope, iElm, iAttrs, controller) {
- }
- };
- });
- </script>
指令中的scope默认为false,那么我们创建的指令就会继承父作用域的一切属性和方法且不隔离,当我们在输入框中输入新的内容的时候,指令中的parentname和父作用域的parentname都会被改变,当我们把scope设置成true时,继承父作用域并且进行隔离:即在控制器中进行数据修改只会影响控制器中的parentname而不会影响到父作用域中的parentname,我们也可以将scope设置成独立作用域进行隔离且不继承,,来看设置成独立作用域后的demo:
- <div ng-controller="parentCtrl">
- <div>{{parentname}}</div>
- <div test parentname="parentname"></div>
- </div>
- <script src="//cdn.bootcss.com/angular.js/1.3.9/angular.min.js"></script>
- <script>
- var routeApp = angular.module('routeApp', []);
- routeApp.controller('parentCtrl',['$scope',function($scope){
- $scope.parentname = "这里是父级控制器里面的内容";
- }]);
- routeApp.directive('test', function(){
- return {
- scope:{
- parentname:'='
- },
- template: '<div><div>{{parentname}}</div><input type="text" ng-model="parentname" /></div>',
- link: function($scope, iElm, iAttrs, controller) {
- }
- };
- });
- </script>
可以看到,写法有些变化,scope:{parentname:'='}使用=实现数据的双向绑定,即在指令中的输入框中输入内容不仅会改变指令中的parentname也会改变父作用域中的parentname。当然,有数据的双向绑定,肯定也会有数据的单项绑定,请看demo:
- <div ng-controller="parentCtrl">
- <div>{{parentname}}</div>
- <div test parentname="{{parentname}}"></div>
- </div>
- <script src="//cdn.bootcss.com/angular.js/1.3.9/angular.min.js"></script>
- <script>
- var routeApp = angular.module('routeApp', []);
- routeApp.controller('parentCtrl',['$scope',function($scope){
- $scope.parentname = "这里是父级控制器里面的内容";
- }]);
- routeApp.directive('test', function(){
- return {
- scope:{
- parentname:'@'
- },
- template: '<div><div>{{parentname}}</div><input type="text" ng-model="parentname" /></div>',
- link: function($scope, iElm, iAttrs, controller) {
- }
- };
- });
- </script>
这样的话在输入框中输入内容,只会改变控制器中的parentname而不会改变父作用域中的parentname。
angular服务实现控制器之间数据交互
在ng中,服务是一个单例,所以需要在服务中生成一个对象,该对象就可以利用依赖注入的方式将数据在所有的控制器中进行共享,下面请来看demo:
- <div ng-controller="brotherCtrl_first">
- <input type="text" ng-model="test"/>
- <div class="btn" ng-click="change()">click me</div>
- </div>
- <div ng-controller="brotherCtrl_second">
- <div class="btn" ng-click="add()">my name {{name}}</div>
- </div>
- <script src="//cdn.bootcss.com/angular.js/1.3.9/angular.min.js"></script>
- <script>
- var routeApp = angular.module('routeApp', []);
- routeApp.factory('instance',function(){
- return {
- name:''
- }
- });
- routeApp.controller('brotherCtrl_first',['$scope','instance',function($scope, instance) {
- $scope.change = function(){
- instance.name = $scope.test;
- };
- }]);
- routeApp.controller('brotherCtrl_second', ['$scope','instance',function($scope, instance) {
- $scope.add = function() {
- $scope.name = instance.name;
- };
- }]);
- </script>
在brotherCtrl_first控制器中的输入框中输入内容点击click me 按钮,这个时候会把输入框中的内容存放到服务中,然后我们在brotherCtrl_second控制器中点击按钮,则会从服务中把数据取出来传给name,从而实现数据的交互。
在angular中的数据交互方式大自是这么几种,如果有说的不好的地方,还请指教。