Editar : o problema abordado nesta resposta foi resolvido na versão 1.2.7 do angular.js . $broadcast
agora evita borbulhar em escopos não registrados e roda tão rápido quanto $ emitem.
Então agora você pode:
- use a
$broadcast
partir do$rootScope
- ouça usando
$on
do local$scope
que precisa saber sobre o evento
Resposta original abaixo
Eu recomendo não usar $rootScope.$broadcast
+, $scope.$on
mas sim $rootScope.$emit
+ $rootScope.$on
. O primeiro pode causar sérios problemas de desempenho, conforme levantado pelo @numan. Isso ocorre porque o evento passará por todos os escopos.
No entanto, o último (usando $rootScope.$emit
+ $rootScope.$on
) não sofre com isso e pode, portanto, ser usado como um canal de comunicação rápido!
A partir da documentação angular de $emit
:
Distribui um nome de evento para cima pela hierarquia de escopo, notificando o registro
Como não há escopo acima $rootScope
, não há bolhas acontecendo. É totalmente seguro usar $rootScope.$emit()
/ $rootScope.$on()
como um EventBus.
No entanto, há um problema ao usá-lo nos Controladores. Se você se vincular diretamente a $rootScope.$on()
partir de dentro de um controlador, precisará limpar a ligação quando seu local $scope
for destruído. Isso ocorre porque os controladores (em contraste com os serviços) podem ser instanciados várias vezes ao longo da vida útil de um aplicativo, o que resultaria em ligações que acabariam criando eventualmente vazamentos de memória em todo o lugar :)
Para cancelar o registro, basta ouvir em seu $scope
's $destroy
evento e, em seguida, chamar a função que foi devolvido pelo $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);
}
]);
Eu diria que isso não é realmente uma coisa específica angular, pois também se aplica a outras implementações do EventBus, que você precisa limpar os recursos.
No entanto, você pode facilitar sua vida para esses casos. Por exemplo, você pode usar o patch do macaco $rootScope
e atribuir um $onRootScope
que assine os eventos emitidos no $rootScope
mas também limpe diretamente o manipulador quando o local $scope
for destruído.
A maneira mais limpa de fazer o patch do macaco $rootScope
para fornecer esse $onRootScope
método seria através de um decorador (um bloco de execução provavelmente também funcionará bem, mas pssst, não conte a ninguém)
Para garantir que a $onRootScope
propriedade não apareça inesperada ao enumerar $scope
, usamos Object.defineProperty()
e configuramos enumerable
como false
. Lembre-se de que você pode precisar de um calço 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;
}]);
}]);
Com este método, o código do controlador acima pode ser simplificado para:
angular
.module('MyApp')
.controller('MyController', ['$scope', function MyController($scope) {
$scope.$onRootScope('someComponent.someCrazyEvent', function(){
console.log('foo');
});
}
]);
Portanto, como resultado final de tudo isso, recomendo que você use $rootScope.$emit
+ $scope.$onRootScope
.
Aliás, estou tentando convencer a equipe angular a resolver o problema dentro do núcleo angular. Há uma discussão em andamento aqui: https://github.com/angular/angular.js/issues/4574
Aqui está um jsperf que mostra o quanto de um impacto de desempenho $broadcast
traz para a mesa em um cenário decente com apenas 100 $scope
's.
http://jsperf.com/rootscope-emit-vs-rootscope-broadcast