Edycja : Problem rozwiązany w tej odpowiedzi został rozwiązany w wersji angular.js w wersji 1.2.7 . $broadcast
teraz unika bulgotania się nad niezarejestrowanymi zakresami i działa tak szybko, jak $ emit.
Teraz możesz:
- użyj
$broadcast
z$rootScope
- słuchaj za pomocą
$on
lokalnego,$scope
który musi wiedzieć o wydarzeniu
Oryginalna odpowiedź poniżej
Radzę nie używać $rootScope.$broadcast
+, $scope.$on
ale raczej $rootScope.$emit
+ $rootScope.$on
. To pierwsze może powodować poważne problemy z wydajnością, o czym wspomniał @numan. Dzieje się tak, ponieważ wydarzenie rozbije się na wszystkie zakresy.
Jednak to ostatnie (użycie $rootScope.$emit
+ $rootScope.$on
) nie cierpi z tego powodu i dlatego może być używane jako szybki kanał komunikacji!
Z dokumentacji kątowej $emit
:
Wysyła nazwę zdarzenia w górę poprzez hierarchię zasięgu, powiadamiając zarejestrowanego
Ponieważ nie ma powyższego zakresu $rootScope
, nie ma miejsca na bulgotanie. Używanie $rootScope.$emit()
/ $rootScope.$on()
jako EventBus jest całkowicie bezpieczne .
Jest jednak jedna gotcha, gdy używasz go z poziomu kontrolerów. Jeśli łączysz się bezpośrednio $rootScope.$on()
z kontrolerem, musisz samodzielnie usunąć powiązanie, gdy lokalny $scope
zostanie zniszczony. Wynika to z faktu, że kontrolery (w przeciwieństwie do usług) mogą być wielokrotnie tworzone przez cały czas istnienia aplikacji, co powoduje zsumowanie powiązań, co ostatecznie spowoduje przeciek pamięci w całym miejscu :)
Aby wyrejestrować, tylko słuchać na swojej $scope
„s $destroy
zdarzenia, a następnie wywołać funkcję, który został zwrócony przez $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);
}
]);
Powiedziałbym, że tak naprawdę nie jest to kwestia specyficzna pod kątem, ponieważ dotyczy to również innych implementacji EventBus, że musisz oczyścić zasoby.
W takich przypadkach możesz jednak ułatwić sobie życie. Na przykład, możesz małpować łatkę $rootScope
i dać jej $onRootScope
subskrypcję zdarzeń emitowanych na, $rootScope
ale także bezpośrednio czyści program obsługi, gdy lokalny $scope
zostanie zniszczony.
Najczystszym sposobem na załatanie małpy w $rootScope
celu zapewnienia takiej $onRootScope
metody byłoby zastosowanie dekoratora (blok uruchamiania prawdopodobnie zrobi to dobrze, ale pssst, nie mów nikomu)
Aby upewnić się, że $onRootScope
właściwość nie pojawi się nieoczekiwanie podczas wyliczania ponad $scope
, używamy Object.defineProperty()
i ustawiamy enumerable
na false
. Pamiętaj, że możesz potrzebować podkładki 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;
}]);
}]);
Dzięki tej metodzie kod kontrolera z góry można uprościć w celu:
angular
.module('MyApp')
.controller('MyController', ['$scope', function MyController($scope) {
$scope.$onRootScope('someComponent.someCrazyEvent', function(){
console.log('foo');
});
}
]);
Dlatego jako ostateczny wynik tego wszystkiego radzę użyć $rootScope.$emit
+ $scope.$onRootScope
.
Przy okazji, staram się przekonać zespół kątowy do rozwiązania problemu w rdzeniu kątowym. Trwa dyskusja tutaj: https://github.com/angular/angular.js/issues/4574
Oto jsperf, który pokazuje, jak duży wpływ $broadcast
na stół przynosi porządny scenariusz w zaledwie 100 $scope
.
http://jsperf.com/rootscope-emit-vs-rootscope-broadcast