I came across a bitch of a problem where I needed to have an isolated scope (related to a Directive) register itself to listen to events dispatched from the $rootScope.
If you're here, you probably know that in order to listen on events you have to be chained in the scope hierarchy, but hey! no, I want my scope to be isolated. you see? a problem.
So I opened up Fiddle and started messing around trying to find some kind of a workaround, and what do you know, I came up with something which I believe is a good start.
Let me explain the concept and from there you can check out the code -
So, basically I wanted to listen for events which some service dispatches (don't be a smart ass and ask why events, just trust I had my own good reasons...). The thing is I wanted to make sure that once the isolated scope is destroyed, it will not leave it's garbage hanging and it's listeners will die with it.
and so, I'm using the service to add a listener to it. when doing that I'm giving the method 2 arguments - the isolated scope and the callback name. On the service I'm grabbing these arguments and create a listener on the $rootScope with the callback I got. If you look closely on the documentation you'll see that the $on API returns a function which is used to de-registrate for this newly created listener, so I'm keeping a map in my service, where each scope is an object which can have many properties (named by it's callback function name) - each holds the de-registration function for it's listener. cool.
You can see the code here on Fiddle: http://jsfiddle.net/YQcGG/11/
And you can also see it here -
This is the HTML:
And this is the rest of it:
var myApp = angular.module('myApp', []);
myApp.factory('myService', function ($rootScope, $timeout) {
return {
listeners: {},
dispatch: function () {
$rootScope.$emit('something-changed');
},
addListener: function (scope, callbackName) {
console.log('adding event to', scope.$id);
var me = this;
if (!me.listeners[scope.$id]) {
me.listeners[scope.$id] = {};
}
me.listeners[scope.$id][callbackName] = $rootScope.$on('something-changed', scope[callbackName]);
scope.$on("$destroy", function () {
for (var item in me.listeners[scope.$id]) {
me.listeners[scope.$id][item]();
}
});
console.log($rootScope.$$listeners);
}
};
});
myApp.controller('myCtrl', function ($rootScope, $scope, myService) {
$scope.removeDtrv = function () {
var div = document.querySelector('my-dtrv');
var e = angular.element(div);
e.scope().$destroy();
e.remove();
console.log($rootScope.$$listeners);
};
$scope.dispatchEvent = function () {
myService.dispatch();
};
});
myApp.directive('myDtrv', function (myService) {
return {
restrict: 'E',
scope: {},
link: function (scope, element, attrs) {
scope.myCallback = function (e, args) {
console.log('I got an event', e);
};
myService.addListener(scope, 'myCallback');
},
template: 'Events to an isolated scope'
};
});
It is not perfect but it is a good start.
Hope this helps you.
Take care and do play some music, ok? :)
Follow @flashmattic