这是一个面试官问我的题目,当时没回答出来,主要是项目中没有碰到过这类问题,因此今天整理下查找到的资料,希望对大家有所帮助。
控制器间的通信大致可以分为三种情况:
1.无直接关联的控制器;
2.父控制器到子控制器;
3.子控制器到父控制器。
方法一、单例服务
单例服务是AngularJS本身支持的数据和代码共享方式,因为是单例的,所有的控制器访问的是同一份数据,我们可以封装成一个服务进行调用,代码如下:
angular.module("app")
.controller("objectService", [objectService]);
function objectService() {
var list = {}; //定义一个hash表
return {
get: function(key) {
return list[key];
},
set: function(key, val) {
list[key] = val;
}
}
}
当我们存入一份数据比如i = 1的时候,可以在当前控制器中直接使用objectService.set('i', 1)即可,在其它控制器中调用的时候直接使用objectService.get('i')就可以获得我们存入的i的数值。
方法二、广播与事件
AngularJs在触发事件和发送广播的时候都可以传递参数,我们可以利用这一特性在控制器之间进行通信。与事件和广播相关的共有三个方法:
1.$emit():在一个$emit()事件函数的调用中,事件从子作用域冒泡到父作用域。在产生事件作用域之上的所有作用域都会收到这个事件的通知,包括$rootScope。$emit语法:
$emit("name", data)
name:要冒泡的事件名称
data:要传递的参数(可以是一个集合)
2.$broadcast():把事件向下传递(从父级作用域到子级作用域),它可以向下传递数据,包括$rootScope向任意控制器传递数据。这里要注意,如果使用了$broadcast方法后就无法取消事件的发送了。$broadcast语法:
$broadcast("name", data)
name:要广播的事件名称
data:要传递的参数(可以是一个集合)
3.$on():监听事件与广播,可以捕获$emit和$broadcast传递的event和data。$on语法:
$on("name", function(data, event){})
name:需要捕获的事件名称
data:捕获到的数据
event:捕获的事件,它有如下方法可用:
1)event.name:捕获到的事件名称
2)event.targetScope:发送或者广播事件的作用域
3)event.currentScope:当前正在处理事件的作用域
4)event.stopPropagation():取消通过$emit触发的事件的进一步传播
5)event.preventDefault():把defaultPrevented设置为true,但它不能停止事件的传播,它会让子作用域不去处理该事件。
6)event.defaultPrevented:获取defaultPrevented的布尔值
PS:上面三种事件方法除了$on可以直接使用外,$emit和$broadcast都必须依赖其他事件(ng-click等)进行触发。
1.子控制器到父控制器
//父控制器
angular.module("app")
.controller("ParentController", ['$scope', ParentController]);
function ParentController($scope) {
$scope.$on("login", function(event, data) {
console.log(data); //打印出{name: "user"}
});
}
//子控制器
angular.module("app")
.controller("ChildController", ['$scope', ChildController]);
function ChildController($scope) {
$scope.data = {name: "user"};
$scope.login = function() {
$scope.$emit("login", $scope.data);
};
}
2.父控制器到子控制器
//父控制器
angular.module("app")
.controller("ParentController", ['$scope', ParentController]);
function ParentController($scope) {
$scope.data = {name: "user"};
$scope.login = function() {
$scope.$broadcast("login", $scope.data);
}
}
//子控制器
angular.module("app")
.controller("ChildController", ['$scope', ChildController]);
function ChildController($scope) {
$scope.$on("login", function(event, data) {
console.log(data); //打印出{name: "user"}
});
}
3.兄弟控制器之间
//父控制器
angular.module("app")
.controller("ParentController", ['$scope', ParentController]);
function ParentController($scope) {
$scope.$on("login", function(event, data) {
$scope.$broadcast("entry", data);
});
}
//传递数据的子控制器
angular.module("app")
.controller("OneChildController", ['$scope', OneChildController]);
function OneChildController($scope) {
$scope.data = {name: "user"};
$scope.login = function() {
$scope.$emit("login", $scope.data);
}
}
//接收数据的子控制器
angular.module("app")
.controller("TwoChildController", ['$scope', TwoChildController]);
function TwoChildController($scope) {
$scope.$on("entry", function(event, data) {
console.log(data); //打印出{name: "user"}
})
}
上面的方法采用的是利用父控制器为中介进行数据的传递。首先我们一个兄弟控制器向父作用域触发一个事件,然后在父作用域中监听事件,再广播给子作用域,这样通过事件携带的参数,实现了数据经过父作用域,在兄弟作用域之间传播。这里要注意的是,通过父元素作为中介进行传递的话,兄弟元素用的事件名不能一样,否则会进入死循环。
方法三、作用域继承
由于作用域的继承是基于JS的原型继承方式,所以这里分为两种情况:
1.当作用域上面的值为基本类型的时候,修改父作用域上面的值会影响到子作用域,反之,修改子作用域只会影响子作用域的值,不会影响父作用域上面的值。
2.如果需要父作用域与子作用域共享一个值的话,就需要用到后面一种,即作用域上的值为对象,任何一方的修改都能影响另一方,这是因为在JS中对象都是引用类型。
基本类型:
function Sandcrawler($scope) {
$scope.location = "Mos Eisley North";
$scope.move = function(newLocation) {
$scope.location = newLocation;
}
}
function Droid($scope) {
$scope.sell = function(newLocation) {
$scope.location = newLocation;
}
}
// html
Location: {{ location }}
Move
Location: {{ location }}
Sell
对象:
function Sandcrawler($scope) {
$scope.obj = {location:"Mos Eisley North"};
}
function Droid($scope) {
$scope.summon = function(newLocation) {
$scope.obj.location = newLocation;
}
}
// html
Sandcrawler Location: {{ location }}
Summon Sandcrawler
方法四、全局或共用的变量
AngularJS提供了对window和localStorage两个变量的封装,$window和$localStorage,通过修改和监听这两个值,可以达到在控制器之间数据共享和通信的目的。方法如下:
//one controller
angular.module("app")
.controller("OneController", ['$scope', '$window', OneController]);
function OneController($scope, $window) {
$scope.$window.data = 1;
}
//other controller
angular.module("app")
.controller("OtherController", ['$scope', '$window', OtherController]);
function OtherController($scope, $window) {
$scope.$watch(function() {
return $window.data;
},
function(n) {
$scope.windowData = n; //获取到data的值并赋给windowData
});
}
利用$window和本地存储也可以进行数据传递:
angular.module("app").factory("local", ['$window', function($window) {
return {
//存储单个属性
set: function(key, val) {
$window.localStorage[key] = val;
},
//读取单个属性
get: function(key, defaultValue) {
return $window.localStorage[key] || defaultValue;
},
//存储对象
setObj: function(key, val) {
$window.localStorage[key] = JSON.stringify(val);
},
//读取对象
getObj: function(key) {
return JSON.parse($window.localStorage[key] || {});
}
}
}]);
方法五、利用$state.params
不同页面的控制器间进行数据传递时,可以利用$state.go或者ui-sref进行数据传递:
在需要传递数据的控制器里,使用$state.go跳转页面时,设置需要传递的数据:
$state.go("urlName", {data: "user"}
html里ui-sref传递:
ui-sref=“urlName({data: 'user'})”
在接收数据的页面使用$scope.$state.params.data即可获取到参数值。
方法六、元素绑定
AngularJS中,可以通过一个元素,来获取其上的控制器实例。通过这种方式便可以快速的获取修改某个控制器中的数据,或者调用这个控制器中的方法。比如:
可以通过以下的方法,来获取控制器实例:
var instance = angular.element(document.getElementById('div-a')).scope();
接着,便可以通过这个instance来调用控制器的方法获取和修改值了。无论是元素本身绑定有控制器,还是元素的父级元素绑定有控制器,都可以成功的获取。
如果你在本文中发现错误或者有异议的地方,可以在评论区留言,谢谢!