Modifier : Le problème abordé dans cette réponse a été résolu dans angular.js. version 1.2.7 . $broadcast
évite maintenant les bulles sur les scopes non enregistrés et s'exécute aussi rapidement que $emit.
Donc, maintenant vous pouvez :
- utiliser
$broadcast
de la $rootScope
- écouter en utilisant
$on
du local $scope
qui doit être informé de l'événement
Réponse originale ci-dessous
Je conseille vivement de ne pas utiliser $rootScope.$broadcast
+ $scope.$on
mais plutôt $rootScope.$emit
+ $rootScope.$on
. La première solution peut entraîner de graves problèmes de performance, comme l'a souligné @numan. Cela est dû au fait que l'événement va se répercuter sur l'ensemble de la chaîne. tous les champs d'application.
Cependant, cette dernière (utilisant $rootScope.$emit
+ $rootScope.$on
) fait no en souffrent et peuvent donc être utilisés comme un canal de communication rapide !
D'après la documentation angulaire de $emit
:
Distribue un nom d'événement vers le haut dans la hiérarchie de l'étendue, en notifiant l'utilisateur enregistré.
Puisqu'il n'y a pas de portée au-dessus de $rootScope
il n'y a pas de bulles. Il est totalement sûr à utiliser $rootScope.$emit()
/ $rootScope.$on()
comme un EventBus.
Cependant, il y a un problème lorsque vous l'utilisez à partir des contrôleurs. Si vous liez directement à $rootScope.$on()
à l'intérieur d'un contrôleur, vous devrez nettoyer vous-même la liaison lorsque votre contrôleur local $scope
est détruit. En effet, les contrôleurs (contrairement aux services) peuvent être instanciés plusieurs fois au cours de la durée de vie d'une application, ce qui aurait pour conséquence que les liaisons s'additionnent et créent des fuites de mémoire partout :)
Pour vous désenregistrer, il suffit d'écouter sur votre $scope
's $destroy
et ensuite appeler la fonction qui a été retournée par $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);
}
]);
Je dirais que ce n'est pas vraiment une spécificité d'Angular, car cela s'applique aussi à d'autres implémentations d'EventBus, que vous devez nettoyer les ressources.
Cependant, vous peut vous faciliter la vie pour ces cas. Par exemple, vous pouvez faire un patch singe $rootScope
et lui donner un $onRootScope
qui souscrit aux événements émis sur le $rootScope
mais aussi de nettoyer directement le gestionnaire lorsque l'option locale $scope
est détruit.
La façon la plus propre de faire un "monkey patch" sur le $rootScope
pour fournir ces $onRootScope
se fera par le biais d'un décorateur (un bloc d'exécution fera probablement aussi bien l'affaire, mais ne le dites à personne).
Pour s'assurer que le $onRootScope
n'apparaît pas de manière inattendue lors de l'énumération de la propriété $scope
nous utilisons Object.defineProperty()
et mettre enumerable
a false
. N'oubliez pas que vous pourriez avoir besoin d'une cale 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;
}]);
}]);
Avec cette méthode en place, le code du contrôleur ci-dessus peut être simplifié :
angular
.module('MyApp')
.controller('MyController', ['$scope', function MyController($scope) {
$scope.$onRootScope('someComponent.someCrazyEvent', function(){
console.log('foo');
});
}
]);
En conclusion de tout ceci, je vous conseille vivement d'utiliser $rootScope.$emit
+ $scope.$onRootScope
.
En fait, j'essaie de convaincre l'équipe d'Angular de résoudre ce problème dans le noyau d'Angular. Il y a une discussion en cours ici : https://github.com/angular/angular.js/issues/4574
Voici un jsperf qui montre l'impact d'une perforation $broadcast
apporte à la table dans un scénario décent avec seulement 100 $scope
's.
http://jsperf.com/rootscope-emit-vs-rootscope-broadcast
36 votes
Totalement discutable, mais en Angular, vous devriez toujours utiliser $window au lieu de l'objet fenêtre natif de JS. De cette façon, vous pouvez le stub dans vos tests :)
1 votes
Veuillez consulter mon commentaire dans la réponse ci-dessous à propos de cette question. $broadcast n'est plus plus plus cher que $emit. Voir le lien jsperf que j'ai référencé ici.