Редактировать : проблема, затронутая в этом ответе, была решена в 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
«S $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
метода был бы через декоратор (блок run, вероятно, тоже отлично это сделает, но 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
.
Кстати, я пытаюсь убедить команду разработчиков Angular решить проблему в рамках ядра Angular. Здесь идет обсуждение: https://github.com/angular/angular.js/issues/4574
Вот jsperf, который показывает, сколько перфектного воздействия $broadcast
приносит на стол при достойном сценарии всего за 100 $scope
с.
http://jsperf.com/rootscope-emit-vs-rootscope-broadcast