342 votos

¿Cuál es la forma correcta de comunicarse entre los controladores en AngularJS?

¿Cuál es la forma correcta de comunicarse entre los controladores en AngularJS?

Actualmente estoy usando un horrible fudge involucran 'la ventana':

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

371voto

Christoph Puntos 4662

Edit: El problema abordado en esta respuesta ha sido solucionado en las últimas versiones de angular.js $broadcast ahora evita burbujas más no registrados ámbitos y corre tan rápido como $emiten.

$broadcast performances are identical to $emit with angular 1.2.16


Lo recomiendo encarecidamente no usar $rootScope.$broadcast + $scope.$on , sino $rootScope.$emit+ $rootScope.$on. El primero puede causar serios problemas de rendimiento planteados por @numan. Eso es debido a que el evento se burbuja hacia abajo a través de todos los ámbitos.

Sin embargo, el segundo (utilizando $rootScope.$emit + $rootScope.$on) ¿ no sufren de este y por lo tanto puede ser utilizado como un canal de comunicación rápido!

Desde el ángulo de la documentación de $emit:

Distribuye un evento nombre hacia arriba a través de la jerarquía de ámbito notificar a la registrada

Ya que no hay ámbito supra $rootScope, no hay ningún burbujeo sucediendo. Es totalmente seguro para el uso $rootScope.$emit()/ $rootScope.$on() como EventBus.

Sin embargo, hay un problema cuando se utiliza dentro de los Controladores. Si se unen directamente a $rootScope.$on() desde dentro de un controlador, usted tendrá que limpiar el enlace cuando el local $scope se destruye. Esto es debido a que los controladores (en contraste a los servicios) puede ser instanciado varias veces durante la vida útil de una aplicación que podría resultar en enlaces resumiendo, finalmente, la creación de fugas de memoria en todo el lugar :)

Para anular el registro, acabo de escuchar en su $scopes' $destroy evento y, a continuación, llamar a la función que devolvió $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);
        }
    ]);

Quiero decir que, realmente, no es un angular de la cosa específica como se aplica a otras EventBus implementaciones así, que tienes que limpiar los recursos.

Sin embargo, usted puede hacer su vida más fácil para aquellos casos. Por ejemplo, usted podría mono parche $rootScope y darle un $onRootScope que se adhiere a los eventos emitidos en el $rootScope pero también directamente limpia el controlador cuando el local $scope se destruye.

La forma más limpia de monkey parche de la $rootScope a proporcionar dicha $onRootScope método sería a través de un decorador (a ejecutar el bloque probablemente hará bien así, pero pssst, no digas a nadie)

Para asegurarse de que el $onRootScope de la propiedad no se muestra inesperado, cuando la enumeración sobre $scope usamos Object.defineProperty() y establezca enumerable a false. Tenga en cuenta que usted puede ser que necesite un ES5 calza.

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

Con este método el código de controlador de arriba se puede simplificar a:

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

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

Así como un resultado final de todo esto me es altamente recomendable utilizar $rootScope.$emit + $scope.$onRootScope.

Por cierto, estoy tratando de convencer a los angular equipo para abordar el problema dentro angular del núcleo. Hay una discusión aquí: https://github.com/angular/angular.js/issues/4574

Aquí es un jsperf que muestra la cantidad de un perf impacto $broadcasttrae a la mesa en un digno escenario con apenas 100 $scopes'.

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

jsperf results

49voto

El uso de $rootScope.$difusión y $scope.$en un PubSub de comunicación.

También, ver este post: AngularJS - la Comunicación Entre los Controladores

36voto

Singo Puntos 181

Desde defineProperty ha navegador problema de compatibilidad, creo que podemos pensar en el uso de un servicio.

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

y lo utilice en el controlador como este:

  • el controlador 1

    function($scope, msgBus) {
        $scope.sendmsg = function() {
            msgBus.emitMsg('somemsg')
        }
    }
    
  • controlador de 2

    function($scope, msgBus) {
        msgBus.onMsg('somemsg', $scope, function() {
            // your logic
        });
    }
    

19voto

Ryan Schumacher Puntos 1046

GridLinked publicado un PubSub solución que parece estar diseñado bastante bien. El servicio puede ser encontrado aquí.

También un diagrama de su servicio:

Messaging Service

15voto

numan salati Puntos 4684

En realidad el uso de emitir y difundir es ineficiente debido a que el evento burbujas hacia arriba y hacia abajo en la jerarquía de ámbito que puede degradar el rendimiento en bottlement para una aplicación compleja.

Yo sugeriría a la utilización de un servicio. Así es como he llevado a cabo recientemente en uno de mis proyectos - https://gist.github.com/3384419.

La idea básica - el registro de un pubsub/bus de eventos como un servicio. A continuación, inyectar que eventbus donde quiera que la necesite para suscribirse o publicar eventos/temas.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X