-
Notifications
You must be signed in to change notification settings - Fork 27.3k
Provide $scope.$onRootScope method #4574
Description
Many people are afraid of using angulars event mechanism as an EventBus
as they fear of performance issues because of it's bubbling behavior.
However, those concerns don't apply if only $rootScope.$emit
/ $rootScope.$on
is used as there is no bubbling happening since $emit only bubbles upwards and there is no scope above the $rootScope
.
I addressed this in my answer on the same thread.
In fact if eventing is used like that in an angular application than $rootScope
is no different than the typical EventBus
pattern. This comes with the drawback of manually having to unregister handlers from within controllers. Why that? Because controllers aren't singletons in Angular and therefore one needs to listen to the local $scope
's $destroy
event and then unregister from the event emitted by the $rootScope
.
So controller code would look like this:
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);
}
]);
As stated in my SO post one can monkey patch the $rootScope
to provide an alternative to it's $on
method that takes an additional parameter with an $scope
to listen for it's $destroy
event to then do the deregistration for us.
angular
.module('MyApp')
.config(['$provide', function($provide){
$provide.decorator('$rootScope', ['$delegate', function($delegate){
$delegate.$saveOn = function(name, listener, scope){
var unsubscribe = $delegate.$on(name, listener);
if (scope){
scope.$on('$destroy', unsubscribe);
}
};
return $delegate;
}]);
}]);
With this in place we can subscribe to events emitted by the $rootScope
and have them automatically deregistered when our local $scope
is being destroyed.
We can use it like this:
angular
.module('MyApp')
.controller('MyController', ['$scope', '$rootScope', function MyController($scope, $rootScope) {
$rootScope.$saveOn('someComponent.someCrazyEvent', function(){
console.log('foo');
}, $scope);
}
]);
However, I think we could do much better by providing a $onRootScope
method directly on the Scope
type so that it's available on every $scope
. This would then automatically make the deregistration for us but we wouldn't have to pass the $scope
explicitly.
It would simply look like this.
angular
.module('MyApp')
.controller('MyController', ['$scope', function MyController($scope) {
$scope.$onRootScope('someComponent.someCrazyEvent', function(){
console.log('foo');
});
}
]);
As far as I know, I can't monkey patch that directly.
Shamelessly pulling @IgorMinar @mhevery @btford and @petebacondarwin and @matsko into this issue ;-)