36

I'm trying to figure out if angular base automatically unbinds watchers and scope events bound with $scope.$on(...) or $scope.$watch(...) when scope is destroyed?

Suppose I have following code:

$scope.$on('someEvents', handleSomeEvent);
$scope.$watch('someProperty', handleSomePropertyChange);

Do I need to manually unbind these watchers and events when $destroy event is triggered on scope?

Ben
  • 54,723
  • 49
  • 178
  • 224
Vytautas Butkus
  • 5,365
  • 6
  • 30
  • 45
  • It depends on where you have set the `$watch`, if it's in a directive, [yes, you do](http://stackoverflow.com/questions/17855203/do-we-need-to-unbind-event-listeners-in-directives-when-angular-starts-to-destro). – glepretre Mar 04 '14 at 11:07

3 Answers3

31

According to Angular documentation on $scope:

'$destroy()' must be called on a scope when it is desired for the scope and its child scopes to be permanently detached from the parent and thus stop participating in model change detection and listener notification by invoking.

Also

Removal also implies that the current scope is eligible for garbage collection.

So it seems when $destroy() is called all the watchers and listeners get removed and the object which represented the scope becomes eligible for garbage collection.

If we look at the destroy() source code we'll see a line :

forEach(this.$$listenerCount, bind(null, decrementListenerCount, this));

Which is supposed to remove all the listeners.

As mentioned by @glepretre it applies to the watchers and listeners in the controller. The same doc page listed above says that:

Note that, in AngularJS, there is also a $destroy jQuery event, which can be used to clean up DOM bindings before an element is removed from the DOM.

So if you have specific listeners in the directives you should listen to the $destroy event and do the necessary cleanup yourself

Igor Malyk
  • 2,646
  • 2
  • 27
  • 34
  • I understand that I need to do it manually for all non angular bindings on $destroy event. Though I missed the part which talks about '...stop participating in model change detection and listener notification...' Thank you – Vytautas Butkus Mar 04 '14 at 12:18
  • Sorry but after reading I still don't understand: $scope.$on and $scope.$watch are automagically unbinded or should I unbind them on the destroy event? – AlfaTeK Aug 27 '14 at 16:36
  • All the watchers on the `$scope` are unbinded automatically. Howerver you should manually unbind listeners on the `$rootScope` and on the DOM elements inside directives. – Igor Malyk Aug 28 '14 at 09:14
  • 1
    you see a pattern some angular files (ng-grid for one) where the angular developers use this to unbind $watch in $scope: `$scope.$on('$destroy', $scope.$watch('renderedColumns', function() { // do stuff }));`, which just passes the function to cancel the $watch directly to the $destroy event listener. – tengen Nov 07 '14 at 16:56
11

If the scope is in a controller, angular unbind for you. Else you can unbind your event by calling the returned function :

var myevent = $scope.$on('someEvents', handleSomeEvent);
myevent() ; // unbind the event

http://docs.angularjs.org/api/ng/function/angular.bind

Maxdow
  • 1,006
  • 11
  • 21
  • How did you find out about this? I am assuming you are speaking for the ``controller`` syntax in a directive as well? Does this also mean any scopes in the link function would not unbind? – Strawberry Apr 04 '15 at 03:14
  • In fact, this automatique deregistration apply on scope in general , in a controller and also for directives. This is because when a controller is deleted or an element is removed from the DOM, a destroy event is triggered. This event unbind all the events on the related scope. That's mean you have to care about event's added on $rootScope because it is never removed. – Maxdow Apr 04 '15 at 12:25
  • You can read some informations here : scope life cycle : https://docs.angularjs.org/guide/scope destroy event : https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$destroy destroy event on elements : https://docs.angularjs.org/api/ng/function/angular.element – Maxdow Apr 04 '15 at 12:30
6

As previously answered, Angular indeed takes care of cleaning things for you, whenever possible. So if you do $scope.$on('someEvents', handleSomeEvent);, once the scope is destroyed (eg when you go to another page/view in your app), the event is automatically removed.

One important thing to note though, is that $rootScope is of course never destroyed, unless you quit your app. So if you do $rootScope.$on('someEvents', handleSomeEvent);, you may have to remove the event yourself, depending on where you listen to the event:

  • if in a controller or directive, then you'll have to remove it manually, else each time you'll instantiate the controller, a new event will be attached, and so handleSomeEvent will be called many times
  • if in a service, then you do not need to remove it manually, as services are always singleton (note that in Angular service, factory, ... all end up being the same thing)
user276648
  • 6,018
  • 6
  • 60
  • 86