Modifica : il problema risolto in questa risposta è stato risolto in angular.js versione 1.2.7 . $broadcast
ora evita il gorgogliamento degli ambiti non registrati e funziona alla stessa velocità di $ emit.
Quindi ora puoi:
- utilizzare
$broadcast
dal$rootScope
- ascolta usando
$on
dal locale$scope
che deve conoscere l'evento
Risposta originale di seguito
Consiglio vivamente di non usare $rootScope.$broadcast
+ $scope.$on
ma piuttosto $rootScope.$emit
+ $rootScope.$on
. Il primo può causare seri problemi di prestazioni sollevati da @numan. Questo perché l'evento passerà attraverso tutti gli ambiti.
Tuttavia, quest'ultimo (usando $rootScope.$emit
+ $rootScope.$on
) non ne soffre e può quindi essere usato come canale di comunicazione veloce!
Dalla documentazione angolare di $emit
:
Invia un nome di evento verso l'alto attraverso la gerarchia dell'ambito che notifica al registrato
Dal momento che non esiste alcun campo di applicazione sopra $rootScope
, non si verificano bolle. È totalmente sicuro da usare $rootScope.$emit()
/ $rootScope.$on()
come EventBus.
Tuttavia, c'è un gotcha quando lo si utilizza da Controller. Se esegui il binding direttamente $rootScope.$on()
da un controller, dovrai ripulire il binding da solo quando sei locale$scope
viene distrutto. Questo perché i controller (a differenza dei servizi) possono essere istanziati più volte nel corso della vita di un'applicazione, il che si tradurrebbe in riassunti di associazioni che alla fine creano perdite di memoria ovunque :)
Per annullare la registrazione, basta ascoltare sul vostro $scope
's $destroy
evento e quindi chiamare la funzione che è stato restituito da $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);
}
]);
Direi che non è una cosa davvero angolare in quanto si applica anche ad altre implementazioni EventBus, che devi ripulire le risorse.
Tuttavia, puoi semplificarti la vita in questi casi. Ad esempio, potresti scimmiottare la patch $rootScope
e dargli un oggetto $onRootScope
che si iscrive agli eventi emessi sul $rootScope
ma pulisce direttamente il gestore quando il locale$scope
viene distrutto.
Il modo più pulito di applicare la patch a una scimmia $rootScope
per fornire tale $onRootScope
metodo sarebbe attraverso un decoratore (un blocco di esecuzione probabilmente lo farà anche bene ma pssst, non dirlo a nessuno)
Per assicurarci che la $onRootScope
proprietà non venga visualizzata in modo imprevisto durante l'enumerazione su $scope
usiamo Object.defineProperty()
e impostiamo enumerable
su false
. Tieni presente che potrebbe essere necessario uno spessore 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 questo metodo, il codice del controller dall'alto può essere semplificato per:
angular
.module('MyApp')
.controller('MyController', ['$scope', function MyController($scope) {
$scope.$onRootScope('someComponent.someCrazyEvent', function(){
console.log('foo');
});
}
]);
Quindi, come risultato finale di tutto ciò, ti consiglio vivamente di usare $rootScope.$emit
+$scope.$onRootScope
.
A proposito, sto cercando di convincere la squadra angolare ad affrontare il problema all'interno del nucleo angolare. C'è una discussione in corso qui: https://github.com/angular/angular.js/issues/4574
Ecco un jsperf che mostra quanto di un impatto perfetto $broadcast
porti sul tavolo in uno scenario decente con soli 100 $scope
anni.
http://jsperf.com/rootscope-emit-vs-rootscope-broadcast