控制器之间通信的正确方法是什么?
我目前正在使用涉及 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);
}
}
19 回答
Edit :此答案中解决的问题已在angular.js version 1.2.7中得到解决 .
$broadcast
现在避免冒泡未注册的范围,并且运行速度与$ emit一样快 .So, now you can:
使用
$broadcast
来自$rootScope
使用需要了解该事件的
$on
from the local $scope 进行监听Original Answer Below
我强烈建议不要使用
$rootScope.$broadcast
$scope.$on
而是$rootScope.$emit
$rootScope.$on
. 前者会导致严重的性能问题,如@numan提出的那样 . 那是因为事件将通过 all 范围向下消失 .但是,后者(使用
$rootScope.$emit
$rootScope.$on
)会受此影响,因此可以用作快速通信通道!从
$emit
的角度文档:由于
$rootScope
之上没有范围,因此没有发生冒泡 . 将$rootScope.$emit()
/$rootScope.$on()
用作EventBus是完全安全的 .但是,在Controllers中使用它时有一个问题 . 如果直接从控制器绑定到
$rootScope.$on()
,则必须在本地$scope
被销毁时自行清理绑定 . 这是因为控制器(与服务相反)可以在应用程序的生命周期内多次实例化,这将导致绑定总结,最终在整个地方创建内存泄漏:)要取消注册,只需听取
$scope
的$destroy
事件,然后调用$rootScope.$on
返回的函数 .我会说,这不是一个特定角度的东西,因为它也适用于其他EventBus实现,你必须清理资源 .
但是,您可以使这些案例的生活更轻松 . 例如,你可以使用补丁
$rootScope
并给它一个$onRootScope
订阅$rootScope
上发出的事件,但也可以在本地$scope
被销毁时直接清理处理程序 .修补
$rootScope
来提供这种$onRootScope
方法的最简洁的方法是通过一个装饰器(一个运行块可能会做得很好但是pssst,不要告诉任何人)为了确保
$onRootScope
属性在枚举$scope
时没有显示意外,我们使用Object.defineProperty()
并将enumerable
设置为false
. 请记住,您可能需要ES5垫片 .有了这种方法,上面的控制器代码可以简化为:
所以作为所有这一切的最终结果,我强烈建议你使用
$rootScope.$emit
$scope.$onRootScope
.顺便说一下,我在这里进行讨论:https://github.com/angular/angular.js/issues/4574
这是一个jsperf,它显示了在一个体面的场景中只有100个
$scope
的表现所带来的影响力 .http://jsperf.com/rootscope-emit-vs-rootscope-broadcast
top answer这是一个解决Angular问题的工作,不再存在(至少版本> 1.2.16和"probably earlier"),正如@zumalifeguard所提到的那样 . 但是,如果没有实际的解决方案,我会离开阅读所有这些答案 .
在我看来,答案现在应该是
使用
$broadcast
来自$rootScope
使用
$on
from the local $scope 监听,需要了解该事件所以要发布
并订阅
Plunkers
Regular $scope syntax(如上所示)
new Controller As syntax
如果在本地
$scope
上注册侦听器,则在删除关联的控制器时将为destroyed automatically by $destroy itself .使用$rootScope.$broadcast和$ scope . $ on进行PubSub通信 .
另外,请看这篇文章:AngularJS – Communicating Between Controllers
由于defineProperty存在浏览器兼容性问题,我认为我们可以考虑使用服务 .
并在控制器中使用它,如下所示:
GridLinked发布了一个PubSub解决方案,似乎设计得很好 . 该服务可以找到,here .
还有他们的服务图:
实际上使用发射和广播是低效的,因为事件在范围层次结构中上下波动,这很容易降级为复杂应用程序的性能瓶装 .
我建议使用服务 . 以下是我最近在其中一个中实现它的方法我的项目 - https://gist.github.com/3384419 .
基本思路 - 将pubsub / event bus注册为服务 . 然后在需要订阅或发布事件/主题的地方注入eventbus .
使用服务中的get和set方法,您可以非常轻松地在控制器之间传递消息 .
在HTML HTML中,您可以像这样检查
关于原始代码 - 您似乎希望在范围之间共享数据 . 要在$ scope范围内共享数据或状态,文档建议使用服务:
运行跨控制器共享的无状态或有状态代码 - 改为使用角度服务 .
实例化或管理其他组件的生命周期(例如,创建服务实例) .
Ref: Angular Docs link here
我实际上已经开始使用Postal.js作为控制器之间的消息总线 .
作为消息总线,如AMQP样式绑定,邮政可以集成w / iFrames和Web套接字的方式,以及更多的东西,它有很多好处 .
我使用装饰器在
$scope.$bus
上设置邮政...这是关于该主题的博客文章的链接......
http://jonathancreamer.com/an-angular-event-bus-with-postal-js/
这是我用Factory / Services和简单的dependency injection (DI)做的 .
我喜欢
$rootscope.emit
用于实现相互通信的方式 . 我建议清洁且性能有效的解决方案,而不会污染全球空间 .这是快速而肮脏的方式 .
这是一个在任何兄弟控制器中添加的示例函数:
当然这是你的HTML:
您可以在模块中的任何位置访问此hello函数
控制器一
第二控制器
More info here
我将创建一个服务并使用通知 .
在Notification Service中创建方法
创建在Notification Service中广播通知的通用方法 .
从源控制器调用notificationService.Method . 如果需要,我还传递相应的对象以保持持久性 .
在方法中,我将数据保留在通知服务中并调用泛型通知方法 .
在目标控制器中,我监听($ scope.on)广播事件并从通知服务访问数据 .
在任何时候,Notification Service都是单身,它应该能够提供持久化数据 .
希望这可以帮助
您可以使用AngularJS内置服务
$rootScope
并在两个控制器中注入此服务 . 然后,您可以侦听在$ rootScope对象上触发的事件 .$ rootScope提供了两个名为
$emit and $broadcast
的事件调度程序,它们负责调度事件(可能是自定义事件)并使用$rootScope.$on
函数添加事件侦听器 .您应该使用该服务,因为
$rootscope
是从整个应用程序访问,它会增加负载,或者如果您的数据不是更多,则使用rootparams .您可以使用$ emit和$ broadcast的角度事件来完成 . 据我们所知,这是最好,最有效和最有效的方法 .
首先,我们从一个控制器调用一个函数 .
您也可以使用$ rootScope代替$ scope . 相应地使用您的控制器
从角度1.5开始,它是基于组件的开发重点 . 组件交互的推荐方法是使用'require'属性和属性绑定(输入/输出) .
组件需要另一个组件(例如根组件)并获得对它的控制器的引用:
然后,您可以在子组件中使用根组件的方法:
这是根组件控制器功能:
这是一个完整的架构概述:Component Communications