What's the correct way to communicate between controllers in AngularJS?

前端 未结 19 2460
猫巷女王i
猫巷女王i 2020-11-21 22:02

What\'s the correct way to communicate between controllers?

I\'m currently using a horrible fudge involving window:

function StockSubgro         


        
相关标签:
19条回答
  • 2020-11-21 22:40

    Using get and set methods within a service you can passing messages between controllers very easily.

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

    in HTML HTML you can check like this

    <div ng-controller='FirstController'>  
    </div>
    
    <div ng-controller='SecondController'>
        {{rslt}}
    </div>
    
    0 讨论(0)
  • 2020-11-21 22:43

    I liked the way how $rootscope.emit was used to achieve intercommunication. I suggest the clean and performance effective solution without polluting global space.

    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');
    
    0 讨论(0)
  • 2020-11-21 22:43

    Here's the quick and dirty way.

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

    Here is an example function to add within any of the sibling controllers:

    $scope.$on('sinkallships', function() {
    
        alert('Sink that ship!');                       
    
    });
    

    and of course here's your HTML:

    <button ngclick="sendorders()">Sink Enemy Ships</button>
    
    0 讨论(0)
  • 2020-11-21 22:45

    You can use AngularJS build-in service $rootScope and inject this service in both of your controllers. You can then listen for events that are fired on $rootScope object.

    $rootScope provides two event dispatcher called $emit and $broadcast which are responsible for dispatching events(may be custom events) and use $rootScope.$on function to add event listener.

    0 讨论(0)
  • 2020-11-21 22:47

    GridLinked posted a PubSub solution which seems to be designed pretty well. The service can be found, here.

    Also a diagram of their service:

    Messaging Service

    0 讨论(0)
  • 2020-11-21 22:48

    Edit: The issue addressed in this answer have been resolved in angular.js version 1.2.7. $broadcast now avoids bubbling over unregistered scopes and runs just as fast as $emit. $broadcast performances are identical to $emit with angular 1.2.16

    So, now you can:

    • use $broadcast from the $rootScope
    • listen using $on from the local $scope that needs to know about the event

    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

    jsperf results

    0 讨论(0)
提交回复
热议问题