Editar : El problema abordado en esta respuesta se ha resuelto en angular.js versión 1.2.7 . $broadcast
ahora evita burbujear sobre ámbitos no registrados y se ejecuta tan rápido como $ emit.
Entonces, ahora puedes:
- uso
$broadcast
de la$rootScope
- escuche usando
$on
el local$scope
que necesita saber sobre el evento
Respuesta original a continuación
Recomiendo no usar $rootScope.$broadcast
+ $scope.$on
sino más bien $rootScope.$emit
+ $rootScope.$on
. El primero puede causar serios problemas de rendimiento como lo plantea @numan. Esto se debe a que el evento burbujeará en todos los ámbitos.
Sin embargo, este último (usando $rootScope.$emit
+ $rootScope.$on
) no sufre esto y, por lo tanto, puede usarse como un canal de comunicación rápido.
De la documentación angular de $emit
:
Envía un nombre de evento hacia arriba a través de la jerarquía de alcance que notifica a los registrados
Como no hay un alcance arriba $rootScope
, no hay burbujeo. Es totalmente seguro usar $rootScope.$emit()
/ $rootScope.$on()
como EventBus.
Sin embargo, hay un problema al usarlo desde Controladores. Si se vincula directamente $rootScope.$on()
desde dentro de un controlador, tendrá que limpiar el enlace usted mismo cuando su local $scope
sea destruido. Esto se debe a que los controladores (en contraste con los servicios) pueden instanciarse varias veces durante la vida útil de una aplicación, lo que daría como resultado que los enlaces se sumen y eventualmente creen pérdidas de memoria por todas partes :)
Para cancelar el registro, simplemente escuchar en su $scope
's $destroy
evento y luego 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);
}
]);
Diría que eso no es realmente algo angular específico, ya que también se aplica a otras implementaciones de EventBus, que tienes que limpiar los recursos.
Sin embargo, puede facilitarle la vida en esos casos. Por ejemplo, podría mono parchear $rootScope
y darle un $onRootScope
que se suscribe a los eventos emitidos en el $rootScope
pero también limpia directamente el controlador cuando el local$scope
se destruye.
La forma más limpia de parchear el mono $rootScope
para proporcionar dicho $onRootScope
método sería a través de un decorador (un bloque de ejecución probablemente también funcionará bien, pero pssst, no se lo digas a nadie)
Para asegurarnos de que la $onRootScope
propiedad no se muestre inesperadamente al enumerar $scope
, usamos Object.defineProperty()
y establecemos enumerable
en false
. Tenga en cuenta que es posible que necesite una cuña 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;
}]);
}]);
Con este método, el código del controlador de arriba se puede simplificar para:
angular
.module('MyApp')
.controller('MyController', ['$scope', function MyController($scope) {
$scope.$onRootScope('someComponent.someCrazyEvent', function(){
console.log('foo');
});
}
]);
Como resultado final de todo esto, le recomiendo que use $rootScope.$emit
+$scope.$onRootScope
.
Por cierto, estoy tratando de convencer al equipo angular para abordar el problema dentro del núcleo angular. Hay una discusión aquí: https://github.com/angular/angular.js/issues/4574
Aquí hay un jsperf que muestra cuánto impacto de rendimiento $broadcast
trae a la mesa en un escenario decente con solo 100 $scope
's.
http://jsperf.com/rootscope-emit-vs-rootscope-broadcast