Редагувати : Проблема, адресована у цій відповіді, була вирішена у angular.js версії 1.2.7 . $broadcast
тепер уникає барботажу над незареєстрованими областями і працює так само швидко, як $ emit.
Отже, тепер ви можете:
- використання
$broadcast
від$rootScope
- слухайте, використовуючи
$on
місцевих,$scope
які повинні знати про подію
Оригінальний відповідь нижче
Я настійно раджу не використовувати $rootScope.$broadcast
+, $scope.$on
а скоріше $rootScope.$emit
+ $rootScope.$on
. Перший може спричинити серйозні проблеми з роботою, які піднімає @numan. Це тому, що подія занепаде всіма сферами.
Однак останній (використовуючи $rootScope.$emit
+ $rootScope.$on
) від цього не страждає і тому може використовуватися як швидкий канал зв'язку!
З кутової документації $emit
:
Відправляє ім'я події вгору за допомогою ієрархії діапазону, що сповіщає про зареєстрованих
Оскільки немає сфери вгорі $rootScope
, не виникає барботаж. Це абсолютно безпечно для використання $rootScope.$emit()
/ $rootScope.$on()
як EventBus.
Однак є одна ґутка при використанні її зсередини контролерів. Якщо ви безпосередньо зв’язуєтеся $rootScope.$on()
зсередини контролера, вам доведеться очистити прив'язку самостійно, коли ваш локальний файл $scope
буде знищений. Це тому, що контролери (на відміну від сервісів) можуть отримувати екземпляри кілька разів протягом життя програми, що призведе до прив'язки, підсумовуючи зрештою, створюючи витоки пам'яті в усьому місці :)
Щоб скасувати реєстрацію, просто прослухайте подію вашої $scope
, $destroy
а потім зателефонуйте до функції, яку повернув $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);
}
]);
Я б сказав, що це насправді не кутова особлива річ, як це стосується й інших реалізацій EventBus, що вам доведеться очищати ресурси.
Однак ви можете полегшити своє життя у цих випадках. Наприклад, ви можете виправити патч мавпи $rootScope
і дати йому $onRootScope
підписку на події, що випромінюються, $rootScope
але також безпосередньо очищає обробник, коли локальний $scope
знищується.
Найчистішим способом промацування мавпи $rootScope
надати такий $onRootScope
метод було б через декоратор (блок запуску, ймовірно, зробить це добре, але pssst, не кажіть нікому)
Для того, щоб переконатися , що $onRootScope
властивість не проявляється несподіваним при перерахуванні більш $scope
ми використовуємо Object.defineProperty()
і встановити enumerable
на false
. Майте на увазі, що вам може знадобитися прокладка 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;
}]);
}]);
За допомогою цього методу замість коду контролера зверху можна спростити:
angular
.module('MyApp')
.controller('MyController', ['$scope', function MyController($scope) {
$scope.$onRootScope('someComponent.someCrazyEvent', function(){
console.log('foo');
});
}
]);
Тож як кінцевий результат усього цього я настійно раджу використовувати $rootScope.$emit
+ $scope.$onRootScope
.
До речі, я намагаюсь переконати команду кутових вирішити проблему в кутовому ядрі. Тут триває дискусія: https://github.com/angular/angular.js/isissue/4574
Ось jsperf, який показує, скільки впливів на парфюмер $broadcast
приносить на стіл у пристойному сценарії всього з 100 $scope
.
http://jsperf.com/rootscope-emit-vs-rootscope-broadcast