是什么在AngularJS控制器之间沟通的正确方法? [英] What's the correct way to communicate between controllers in AngularJS?

查看:285
本文介绍了是什么在AngularJS控制器之间沟通的正确方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

什么是控制器之间沟通的正确方法?

我目前使用涉及到一个可怕的软糖窗口

 函数StockSubgroupCtrl($范围,$ HTTP){
    $ scope.subgroups = [];
    $ scope.handleSubgroupsLoaded =功能(数据,状态){
        $ scope.subgroups =数据;
    }
    $ scope.fetch =功能(prod_grp){
        $ http.get('/ API /股票/组/+ prod_grp +'/subgroups/').success($scope.handleSubgroupsLoaded);
    }
    window.fetchStockSubgroups = $ scope.fetch;
}功能StockGroupCtrl($范围,$ HTTP){
    ...
    $ scope.select =功能(prod_grp){
        $ scope.selectedGroup = prod_grp;
        window.fetchStockSubgroups(prod_grp);
    }
}


解决方案

修改:这个问题在这个回答解决在最新版本的angular.js已经解决了 $广播现在可以避免冒泡未登记的范围和运行一样快$发出。


原文回答以下

我强烈建议不要使用 $ rootScope。$广播 + $范围。$关于而是 $ rootScope。$发出 + $ rootScope。$关于。通过@numan作为提出前者可能会导致严重的性能问题。这是因为本次活动将泡沫向下穿过全部范围。

然而,后者(使用 $ rootScope。$发出 + $ rootScope。$关于)确实<强>不可以此受苦,因此可以用作快速沟通渠道!

从的角度文档 $发出


  

通过范围层次向上通知调度事件名称注册


由于上面有 $ rootScope 无用武之地,没有起泡发生。这是完全可以放心使用 $ rootScope。$放出() / $ rootScope。$的()作为EventBus

然而,存在有一个问题,从控制器内使用时。如果你直接绑定到 $ rootScope。在()从控制器内,你必须清理自己绑定在你的本地 $ $范围被摧毁。这是因为控制器(与服务),可以在应用程序的生命周期,这将导致到最终的绑定创建内存泄漏总结各地的地方得到多次实例:)

要注销,只是听你的 $范围 $摧毁事件,然后调用是功能通过 $ rootScope。$关于返回。

 
    .module('MyApp的')
    .controller('myController的',['$范围,$ rootScope',功能myController的($范围,$ rootScope){            VAR解除绑定= $ rootScope。在$('someComponent.someCrazyEvent',函数(){
                的console.log('富');
            });            $ $范围在('$摧毁,解除绑定)。
        }
    ]);

我会说,因为它适用于其他EventBus的实现,以及,你必须清理资源,这不是一个真正的角具体的事情。

不过,你的可以的让你的生活了这些情况下更容易。例如,您可以猴补丁 $ rootScope ,并给它一个 $ onRootScope 订阅了对<$发出的事件C $ C> $ rootScope 也可直接清除在本地 $范围被销毁处理。

猴子最彻底的方法修补 $ rootScope 来提供这样的 $ onRootScope 的方法是通过一个装饰(一个运行块可能会做到这一点也很不错,但pssst,不要告诉任何人)

要确保列举了 $范围我们使用的时候 $ onRootScope 属性不会出现意外 Object.defineProperty()并设置枚举。请记住,你可能需要一个垫片ES5

 
    .module('MyApp的')
    的.config(['$提供'功能($提供){
        $ provide.decorator('$ rootScope',['$委托',函数($代表){            Object.defineProperty($ delegate.constructor.prototype,'$ onRootScope',{
                值:函数(名称,监听器){
                    VAR退订= $ $代表对(名称,监听器)。
                    这在$('$毁灭',退订)。                    返回退订;
                },
                枚举:假的
            });
            返回$代表;
        }]);
    }]);

使用代替该方法中从上述控制器code可以被简化为:

 
    .module('MyApp的')
    .controller('myController的',['$范围,功能myController的($范围){            $范围。$ onRootScope('someComponent.someCrazyEvent',函数(){
                的console.log('富');
            });
        }
    ]);

因此​​,作为这一切的最终结果,我强烈建议你使用 $ rootScope。$发出 + $范围。$ onRootScope

顺便说一下,我试图说服角度团队解决方芯内的问题。有一个讨论,这里发生了:<一href=\"https://github.com/angular/angular.js/issues/4574\">https://github.com/angular/angular.js/issues/4574

下面是显示多少的PERF的影响jsperf $广播带来的表在一个体面的情况下只有100 $范围

<一个href=\"http://jsperf.com/rootscope-emit-vs-rootscope-broadcast\">http://jsperf.com/rootscope-emit-vs-rootscope-broadcast

What's the correct way to communicate between controllers?

I'm currently using a horrible fudge involving 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);
    }
}

解决方案

Edit: The issue addressed in this answer have been resolved in recent versions of angular.js $broadcast now avoids bubbling over unregistered scopes and runs just as fast as $emit.


Original Answer Below

I highly advise not to use $rootScope.$broadcast + $scope.$on but rather $rootScope.$emit+ $rootScope.$on. The former can cause serious performance problems as raised by @numan. That is because the event will bubble down through all scopes.

However, the latter (using $rootScope.$emit + $rootScope.$on) does not suffer from this and can therefore be used as a fast communication channel!

From the angular documentation of $emit:

Dispatches an event name upwards through the scope hierarchy notifying the registered

Since there is no scope above $rootScope, there is no bubbling happening. It is totally safe to use $rootScope.$emit()/ $rootScope.$on() as an EventBus.

However, there is one gotcha when using it from within Controllers. If you directly bind to $rootScope.$on() from within a controller, you'll have to clean up the binding yourself when your local $scope gets destroyed. This is because controllers (in contrast to services) can get instantiated multiple times over the lifetime of an application which would result into bindings summing up eventually creating memory leaks all over the place :)

To unregister, just listen on your $scope's $destroy event and then call the function that was returned by $rootScope.$on.

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);
        }
    ]);

I would say, that's not really an angular specific thing as it applies to other EventBus implementations as well, that you have to clean up resources.

However, you can make your life easier for those cases. For instance, you could monkey patch $rootScope and give it a $onRootScope that subscribes to events emitted on the $rootScope but also directly cleans up the handler when the local $scope gets destroyed.

The cleanest way to monkey patch the $rootScope to provide such $onRootScope method would be through a decorator (a run block will probably do it just fine as well but pssst, don't tell anybody)

To make sure the $onRootScope property doesn't show up unexpected when enumerating over $scope we use Object.defineProperty() and set enumerable to false. Keep in mind that you might need an ES5 shim.

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;
        }]);
    }]);

With this method in place the controller code from above can be simplified to:

angular
    .module('MyApp')
    .controller('MyController', ['$scope', function MyController($scope) {

            $scope.$onRootScope('someComponent.someCrazyEvent', function(){
                console.log('foo');
            });
        }
    ]);

So as a final outcome of all this I highly advise you to use $rootScope.$emit + $scope.$onRootScope.

Btw, I'm trying to convince the angular team to address the problem within angular core. There's a discussion going on here: https://github.com/angular/angular.js/issues/4574

Here is a jsperf that shows how much of a perf impact $broadcastbrings to the table in a decent scenario with just 100 $scope's.

http://jsperf.com/rootscope-emit-vs-rootscope-broadcast

这篇关于是什么在AngularJS控制器之间沟通的正确方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆