I have registered my listener to a $broadcast event using $on function
$scope.$on("onViewUpdated", this.callMe);
and I want to un-register this listener based on a particular business rule. But my problem is that once it is registered I am not able to un-register it.
Is there any method in AngularJS to un-register a particular listener? A method like $on that un-register this event, may be $off. So that based on the business logic i can say
$scope.$off("onViewUpdated", this.callMe);
and this function stop being called when somebody broadcast "onViewUpdated" event.
Thanks
EDIT: I want to de-register the listener from another function. Not the function where i register it.
This question is related to
angularjs
EDIT: The correct way to do this is in @LiviuT's answer!
You can always extend Angular's scope to allow you to remove such listeners like so:
//A little hack to add an $off() method to $scopes.
(function () {
var injector = angular.injector(['ng']),
rootScope = injector.get('$rootScope');
rootScope.constructor.prototype.$off = function(eventName, fn) {
if(this.$$listeners) {
var eventArr = this.$$listeners[eventName];
if(eventArr) {
for(var i = 0; i < eventArr.length; i++) {
if(eventArr[i] === fn) {
eventArr.splice(i, 1);
}
}
}
}
}
}());
And here's how it would work:
function myEvent() {
alert('test');
}
$scope.$on('test', myEvent);
$scope.$broadcast('test');
$scope.$off('test', myEvent);
$scope.$broadcast('test');
Looking at most of the replies, they seem overly complicated. Angular has built in mechanisms to unregister.
Use the deregistration function returned by $on
:
// Register and get a handle to the listener
var listener = $scope.$on('someMessage', function () {
$log.log("Message received");
});
// Unregister
$scope.$on('$destroy', function () {
$log.log("Unregistering listener");
listener();
});
Register a hook to unsubscribe your listeners when the component is removed:
$scope.$on('$destroy', function () {
delete $rootScope.$$listeners["youreventname"];
});
This code works for me:
$rootScope.$$listeners.nameOfYourEvent=[];
@LiviuT's answer is awesome, but seems to leave lots of folks wondering how to re-access the handler's tear-down function from another $scope or function, if you want to destroy it from a place other than where it was created. @?????? ?????????'s answer works just great, but isn't very idiomatic. (And relies on what's supposed to be a private implementation detail, which could change any time.) And from there, it just gets more complicated...
I think the easy answer here is to simply carry a reference to the tear-down function (offCallMeFn
in his example) in the handler itself, and then call it based on some condition; perhaps an arg that you include on the event you $broadcast or $emit. Handlers can thus tear down themselves, whenever you want, wherever you want, carrying around the seeds of their own destruction. Like so:
// Creation of our handler:
var tearDownFunc = $rootScope.$on('demo-event', function(event, booleanParam) {
var selfDestruct = tearDownFunc;
if (booleanParam === false) {
console.log('This is the routine handler here. I can do your normal handling-type stuff.')
}
if (booleanParam === true) {
console.log("5... 4... 3... 2... 1...")
selfDestruct();
}
});
// These two functions are purely for demonstration
window.trigger = function(booleanArg) {
$scope.$emit('demo-event', booleanArg);
}
window.check = function() {
// shows us where Angular is stashing our handlers, while they exist
console.log($rootScope.$$listeners['demo-event'])
};
// Interactive Demo:
>> trigger(false);
// "This is the routine handler here. I can do your normal handling-type stuff."
>> check();
// [function] (So, there's a handler registered at this point.)
>> trigger(true);
// "5... 4... 3... 2... 1..."
>> check();
// [null] (No more handler.)
>> trigger(false);
// undefined (He's dead, Jim.)
Two thoughts:
selfDestruct
as soon as it has completed its suicide mission.One way is to simply destroy the listener once you are done with it.
var removeListener = $scope.$on('navBarRight-ready', function () {
$rootScope.$broadcast('workerProfile-display', $scope.worker)
removeListener(); //destroy the listener
})
In case that you need to turn on and off the listener multiple times, you can create a function with boolean
parameter
function switchListen(_switch) {
if (_switch) {
$scope.$on("onViewUpdated", this.callMe);
} else {
$rootScope.$$listeners.onViewUpdated = [];
}
}
After debugging the code, i created my own function just like "blesh"'s answer. So this is what i did
MyModule = angular.module('FIT', [])
.run(function ($rootScope) {
// Custom $off function to un-register the listener.
$rootScope.$off = function (name, listener) {
var namedListeners = this.$$listeners[name];
if (namedListeners) {
// Loop through the array of named listeners and remove them from the array.
for (var i = 0; i < namedListeners.length; i++) {
if (namedListeners[i] === listener) {
return namedListeners.splice(i, 1);
}
}
}
}
});
so by attaching my function to $rootscope now it is available to all my controllers.
and in my code I am doing
$scope.$off("onViewUpdated", callMe);
Thanks
EDIT: The AngularJS way to do this is in @LiviuT's answer! But if you want to de-register the listener in another scope and at the same time want to stay away from creating local variables to keep references of de-registeration function. This is a possible solution.
'$on' itself returns function for unregister
var unregister= $rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams, options) {
alert('state changing');
});
you can call unregister() function to unregister that listener
Source: Stackoverflow.com