控制器之间通信的正确方法是什么?
我目前正在使用涉及window
的可怕软糖:
function StockSubgroupCtrl($scope, $http) {
$scope.subgroups = [];
$scope.handleSubgroupsLoaded = function(data, status) {
$scope.subgroups = data;
}
$scope.fetch = function(prod_grp) {
$http.get('/api/stock/groups/' + prod_grp + '/subgroups/').success($scope.handleSubgroupsLoaded);
}
window.fetchStockSubgroups = $scope.fetch;
}
function StockGroupCtrl($scope, $http) {
...
$scope.select = function(prod_grp) {
$scope.selectedGroup = prod_grp;
window.fetchStockSubgroups(prod_grp);
}
}
答案 0 :(得分:453)
修改:此答案中解决的问题已在angular.js version 1.2.7中得到解决。 $broadcast
现在避免冒泡未注册的范围,并且运行速度与$ emit一样快。
所以,现在你可以:
$broadcast
$rootScope
$on
的$scope
进行倾听下面的原始答案
我强烈建议您不要使用$rootScope.$broadcast
+ $scope.$on
,而应使用$rootScope.$emit
+ $rootScope.$on
。前者会导致严重的性能问题,如@numan提出的那样。这是因为该事件将通过所有范围向下消失。
但是,后者(使用$rootScope.$emit
+ $rootScope.$on
)不会受此影响,因此可以用作快速沟通渠道!
来自$emit
的角度文档:
通过范围层次结构向上调度事件名称,通知已注册的
由于$rootScope
以上没有范围,因此不会发生冒泡。将$rootScope.$emit()
/ $rootScope.$on()
用作EventBus是完全安全的。
但是,在Controllers中使用它时有一个问题。如果直接从控制器中绑定到$rootScope.$on()
,则必须在本地$scope
被破坏时自行清理绑定。这是因为控制器(与服务相反)可以在应用程序的生命周期内多次实例化,这会导致绑定总结,最终造成内存泄漏:)
要取消注册,只需听取$scope
的{{1}}事件,然后调用$destroy
返回的函数。
$rootScope.$on
我想说,这不是一个特定角度的东西,因为它也适用于其他EventBus实现,你必须清理资源。
但是,可以让这些案例的生活更轻松。例如,您可以使用补丁angular
.module('MyApp')
.controller('MyController', ['$scope', '$rootScope', function MyController($scope, $rootScope) {
var unbind = $rootScope.$on('someComponent.someCrazyEvent', function(){
console.log('foo');
});
$scope.$on('$destroy', unbind);
}
]);
并为其指定$rootScope
订阅$onRootScope
上发出的事件,但也可以在本地$rootScope
被销毁时直接清理处理程序
最简单的方法是修补$scope
提供这种$rootScope
方法的方法是通过一个装饰器(一个运行块可能会做得很好但是pssst,不要告诉任何人)
为了确保在$onRootScope
枚举时$onRootScope
属性未显示意外,我们使用$scope
并将Object.defineProperty()
设置为enumerable
。请记住,您可能需要ES5垫片。
false
使用此方法,可以将上面的控制器代码简化为:
angular
.module('MyApp')
.config(['$provide', function($provide){
$provide.decorator('$rootScope', ['$delegate', function($delegate){
Object.defineProperty($delegate.constructor.prototype, '$onRootScope', {
value: function(name, listener){
var unsubscribe = $delegate.$on(name, listener);
this.$on('$destroy', unsubscribe);
return unsubscribe;
},
enumerable: false
});
return $delegate;
}]);
}]);
因此,作为所有这一切的最终结果,我强烈建议您使用angular
.module('MyApp')
.controller('MyController', ['$scope', function MyController($scope) {
$scope.$onRootScope('someComponent.someCrazyEvent', function(){
console.log('foo');
});
}
]);
+ $rootScope.$emit
。
这是一个jsperf,它显示了在一个不错的情况下$scope.$onRootScope
只有100 $broadcast
的影响所带来的影响$scope
。
http://jsperf.com/rootscope-emit-vs-rootscope-broadcast
答案 1 :(得分:103)
top answer这是一个解决Angular问题的工作,该问题已不再存在(至少在版本> 1.2.16和“可能更早”),正如@zumalifeguard所提到的那样。但是,如果没有实际的解决方案,我会离开阅读所有这些答案。
在我看来,答案现在应该是
$broadcast
$rootScope
$on
的$scope
进行倾听所以要发布
// EXAMPLE PUBLISHER
angular.module('test').controller('CtrlPublish', ['$rootScope', '$scope',
function ($rootScope, $scope) {
$rootScope.$broadcast('topic', 'message');
}]);
订阅
// EXAMPLE SUBSCRIBER
angular.module('test').controller('ctrlSubscribe', ['$scope',
function ($scope) {
$scope.$on('topic', function (event, arg) {
$scope.receiver = 'got your ' + arg;
});
}]);
Plunkers
如果您在本地$scope
上注册了侦听器,则删除关联的控制器时将为destroyed automatically by $destroy
itself。
答案 2 :(得分:53)
使用$rootScope.$broadcast和$ scope。$ on进行PubSub通信。
答案 3 :(得分:42)
由于defineProperty存在浏览器兼容性问题,我认为我们可以考虑使用服务。
angular.module('myservice', [], function($provide) {
$provide.factory('msgBus', ['$rootScope', function($rootScope) {
var msgBus = {};
msgBus.emitMsg = function(msg) {
$rootScope.$emit(msg);
};
msgBus.onMsg = function(msg, scope, func) {
var unbind = $rootScope.$on(msg, func);
scope.$on('$destroy', unbind);
};
return msgBus;
}]);
});
并在控制器中使用它:
控制器1
function($scope, msgBus) {
$scope.sendmsg = function() {
msgBus.emitMsg('somemsg')
}
}
控制器2
function($scope, msgBus) {
msgBus.onMsg('somemsg', $scope, function() {
// your logic
});
}
答案 4 :(得分:20)
答案 5 :(得分:15)
实际上使用发射和广播是低效的,因为事件在范围层次结构中上下波动,这很容易降级为复杂应用程序的性能瓶装。
我建议使用服务。以下是我最近在我的一个项目https://gist.github.com/3384419中实现它的方法。
基本思路 - 将pubsub / event总线注册为服务。然后在需要订阅或发布事件/主题的地方注入eventbus。
答案 6 :(得分:14)
在服务中使用get和set方法,您可以非常轻松地在控制器之间传递消息。
var myApp = angular.module("myApp",[]);
myApp.factory('myFactoryService',function(){
var data="";
return{
setData:function(str){
data = str;
},
getData:function(){
return data;
}
}
})
myApp.controller('FirstController',function($scope,myFactoryService){
myFactoryService.setData("Im am set in first controller");
});
myApp.controller('SecondController',function($scope,myFactoryService){
$scope.rslt = myFactoryService.getData();
});
在HTML HTML中,您可以像这样检查
<div ng-controller='FirstController'>
</div>
<div ng-controller='SecondController'>
{{rslt}}
</div>
答案 7 :(得分:8)
关于原始代码 - 您似乎希望在范围之间共享数据。要在$ scope范围内共享数据或状态,文档建议使用服务:
答案 8 :(得分:5)
我实际上已经开始使用Postal.js作为控制器之间的消息总线。
作为消息总线,有很多好处,例如AMQP样式绑定,邮政可以与iFrame和web套接字集成的方式等等。
我使用装饰器在$scope.$bus
...
angular.module('MyApp')
.config(function ($provide) {
$provide.decorator('$rootScope', ['$delegate', function ($delegate) {
Object.defineProperty($delegate.constructor.prototype, '$bus', {
get: function() {
var self = this;
return {
subscribe: function() {
var sub = postal.subscribe.apply(postal, arguments);
self.$on('$destroy',
function() {
sub.unsubscribe();
});
},
channel: postal.channel,
publish: postal.publish
};
},
enumerable: false
});
return $delegate;
}]);
});
这是指向该主题的博客文章的链接...
http://jonathancreamer.com/an-angular-event-bus-with-postal-js/
答案 9 :(得分:3)
这就是我使用Factory / Services和简单dependency injection (DI)。
的方式myApp = angular.module('myApp', [])
# PeopleService holds the "data".
angular.module('myApp').factory 'PeopleService', ()->
[
{name: "Jack"}
]
# Controller where PeopleService is injected
angular.module('myApp').controller 'PersonFormCtrl', ['$scope','PeopleService', ($scope, PeopleService)->
$scope.people = PeopleService
$scope.person = {}
$scope.add = (person)->
# Simply push some data to service
PeopleService.push angular.copy(person)
]
# ... and again consume it in another controller somewhere...
angular.module('myApp').controller 'PeopleListCtrl', ['$scope','PeopleService', ($scope, PeopleService)->
$scope.people = PeopleService
]
答案 10 :(得分:3)
我喜欢$rootscope.emit
如何用来实现互通的方式。我建议使用干净且性能有效的解决方案,而不会污染全球空间。
module.factory("eventBus",function (){
var obj = {};
obj.handlers = {};
obj.registerEvent = function (eventName,handler){
if(typeof this.handlers[eventName] == 'undefined'){
this.handlers[eventName] = [];
}
this.handlers[eventName].push(handler);
}
obj.fireEvent = function (eventName,objData){
if(this.handlers[eventName]){
for(var i=0;i<this.handlers[eventName].length;i++){
this.handlers[eventName][i](objData);
}
}
}
return obj;
})
//Usage:
//In controller 1 write:
eventBus.registerEvent('fakeEvent',handler)
function handler(data){
alert(data);
}
//In controller 2 write:
eventBus.fireEvent('fakeEvent','fakeData');
答案 11 :(得分:2)
这是快速而肮脏的方式。
// Add $injector as a parameter for your controller
function myAngularController($scope,$injector){
$scope.sendorders = function(){
// now you can use $injector to get the
// handle of $rootScope and broadcast to all
$injector.get('$rootScope').$broadcast('sinkallships');
};
}
以下是在任何兄弟控制器中添加的示例函数:
$scope.$on('sinkallships', function() {
alert('Sink that ship!');
});
当然这是你的HTML:
<button ngclick="sendorders()">Sink Enemy Ships</button>
答案 12 :(得分:1)
启动角度1.5,它是基于组件的开发重点。组件交互的推荐方法是使用'require'属性和属性绑定(输入/输出)。
组件需要另一个组件(例如根组件)并获取对其控制器的引用:
angular.module('app').component('book', {
bindings: {},
require: {api: '^app'},
template: 'Product page of the book: ES6 - The Essentials',
controller: controller
});
然后,您可以在子组件中使用根组件的方法:
$ctrl.api.addWatchedBook('ES6 - The Essentials');
这是根组件控制器功能:
function addWatchedBook(bookName){
booksWatched.push(bookName);
}
以下是完整的架构概述:Component Communications
答案 13 :(得分:0)
你可以使用$ emit和$ broadcast的角度事件来做到这一点。据我们所知,这是最好,最有效和最有效的方式。
首先我们从一个控制器调用一个函数。
Private Sub Workbook_BeforeClose(Cancel As Boolean)
'Your Code Here
End Sub
您也可以使用$ rootScope代替$ scope。相应地使用你的控制器。
答案 14 :(得分:0)
function mySrvc() {
var callback = function() {
}
return {
onSaveClick: function(fn) {
callback = fn;
},
fireSaveClick: function(data) {
callback(data);
}
}
}
function controllerA($scope, mySrvc) {
mySrvc.onSaveClick(function(data) {
console.log(data)
})
}
function controllerB($scope, mySrvc) {
mySrvc.fireSaveClick(data);
}
答案 15 :(得分:0)
您可以使用AngularJS内置服务$rootScope
并在两个控制器中注入此服务。
然后,您可以侦听在$ rootScope对象上触发的事件。
$ rootScope提供了两个名为$emit and $broadcast
的事件调度程序,它们负责调度事件(可能是自定义事件)并使用$rootScope.$on
函数添加事件侦听器。
答案 16 :(得分:0)
您应该使用该服务,因为$rootscope
可以从整个应用程序访问,并且会增加负载,或者如果您的数据不多,则使用rootparams。
答案 17 :(得分:0)
我将创建一个服务并使用通知。
在任何时候,Notification Service都是单身,它应该能够提供持久化的数据。
希望这有帮助
答案 18 :(得分:0)
您可以在模块的任何位置访问此hello函数
控制器一
$scope.save = function() {
$scope.hello();
}
第二个控制器
$rootScope.hello = function() {
console.log('hello');
}