scope - AngularJS 中 Controller 之间通信的正确方法是什么?

标签 scope angularjs

Controller 之间通信的正确方法是什么?

我目前正在使用涉及窗口的可怕软糖:

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

最佳答案

编辑:此答案中解决的问题已在 angular.js version 1.2.7 中得到解决。 。 $broadcast现在可以避免在未注册的作用域上冒泡,并且运行速度与 $emit 一样快。 $broadcast performances are identical to $emit with angular 1.2.16

所以,现在您可以:

  • 使用$broadcast来自$rootScope
  • 使用 $on 收听来自本地$scope 需要了解该事件
<小时/>

原始答案如下

我强烈建议不要使用$rootScope.$broadcast + $scope.$on而是$rootScope.$emit + $rootScope.$on 。前者可能会导致严重的性能问题,如 @numan 提出的。这是因为事件将在所有范围内冒泡。

但是,后者(使用 $rootScope.$emit + $rootScope.$on )不会受到此影响,因此可以用作快速通信 channel !

来自$emit的 Angular 文档:

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

由于上面没有范围 $rootScope ,没有发生冒泡。使用完全安全$rootScope.$emit()/$rootScope.$on()作为事件总线。

但是,在 Controller 内使用它时存在一个问题。如果直接绑定(bind)$rootScope.$on()从 Controller 内部,当您的本地 $scope 时,您必须自己清理绑定(bind)。被摧毁。这是因为 Controller (与服务相反)可以在应用程序的生命周期内多次实例化,这将导致绑定(bind)汇总,最终在各处造成内存泄漏:)

要取消注册,只需收听您的 $scope$destroy事件,然后调用 $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);
        }
    ]);

我想说,这并不是一个真正的 Angular 特定的事情,因为它也适用于其他 EventBus 实现,您必须清理资源。

但是,您可以让这些情况变得更轻松。例如,您可以猴子补丁 $rootScope并给它一个 $onRootScope订阅 $rootScope 上发出的事件而且当本地 $scope 时也直接清理处理程序被摧毁。

猴子修补$rootScope的最干净的方法提供这样的$onRootScope方法将通过装饰器(运行 block 可能也可以很好地完成,但是请不要告诉任何人)

为了确保 $onRootScope枚举 $scope 时,属性不会意外显示我们使用Object.defineProperty()并设置enumerablefalse 。请记住,您可能需要 ES5 垫片。

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

使用此方法,上面的 Controller 代码可以简化为:

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

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

因此,作为这一切的最终结果,我强烈建议您使用 $rootScope.$emit + $scope.$onRootScope .

顺便说一句,我正在尝试说服 Angular 团队解决 Angular Core 中的问题。这里正在进行讨论:https://github.com/angular/angular.js/issues/4574

这是一个 jsperf,显示了性能影响有多大 $broadcast只需 100 $scope 就可以在一个不错的场景中实现的。

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

jsperf results

关于scope - AngularJS 中 Controller 之间通信的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11252780/

相关文章:

python - Django & Celery 使用单例类实例属性作为全局

$.getJSON 调用中的 JavaScript 变量作用域

c - 范围问题 : External variable not stored

javascript - 基本 Ajax 库中的错误

具有文件范围的 C 结构体

angularjs - Angular 修改元标记

javascript - 如何防止子 Controller 中 div 上的 ng-click 事件触发根中的 ng-click 事件?

angularjs - angular-ui-tree 中可拖动节点中的可点击链接

javascript - Angular $http.get 请求未运行成功或错误函数

javascript - 如何在 Selenium webdriver 中使用 cssSelector 识别多个参数