https://docs.angularjs.org/guide/directive
By listening to this event, you can remove event listeners that might cause memory leaks. Listeners registered to scopes and elements are automatically cleaned up when they are destroyed, but if you registered a listener on a service, or registered a listener on a DOM node that isn't being deleted, you'll have to clean it up yourself or you risk introducing a memory leak.
Best Practice: Directives should clean up after themselves. You can use element.on('$destroy', ...) or scope.$on('$destroy', ...) to run a clean-up function when the directive is removed.
Question:
I have a element.on "click", (event) ->
inside my directive:
- When the directive is destroyed, are there any memory references to the
element.on
to keep it from being garbage collected? - Angular documentation states that I should use a handler to remove event listeners on the
$destroy
emitted event. I was under the impression thatdestroy()
removed event listeners, is this not the case?
Event listeners
First off it's important to understand that there are two kinds of "event listeners":
Scope event listeners registered via
$on
:Event handlers attached to elements via for example
on
orbind
:$scope.$destroy()
When
$scope.$destroy()
is executed it will remove all listeners registered via$on
on that $scope.It will not remove DOM elements or any attached event handlers of the second kind.
This means that calling
$scope.$destroy()
manually from example within a directive's link function will not remove a handler attached via for exampleelement.on
, nor the DOM element itself.element.remove()
Note that
remove
is a jqLite method (or a jQuery method if jQuery is loaded before AngularjS) and is not available on a standard DOM Element Object.When
element.remove()
is executed that element and all of its children will be removed from the DOM together will all event handlers attached via for exampleelement.on
.It will not destroy the $scope associated with the element.
To make it more confusing there is also a jQuery event called
$destroy
. Sometimes when working with third-party jQuery libraries that remove elements, or if you remove them manually, you might need to perform clean up when that happens:What to do when a directive is "destroyed"
This depends on how the directive is "destroyed".
A normal case is that a directive is destroyed because
ng-view
changes the current view. When this happens theng-view
directive will destroy the associated $scope, sever all the references to its parent scope and callremove()
on the element.This means that if that view contains a directive with this in its link function when it's destroyed by
ng-view
:Both event listeners will be removed automatically.
However, it's important to note that the code inside these listeners can still cause memory leaks, for example if you have achieved the common JS memory leak pattern
circular references
.Even in this normal case of a directive getting destroyed due to a view changing there are things you might need to manually clean up.
For example if you have registered a listener on
$rootScope
:This is needed since
$rootScope
is never destroyed during the lifetime of the application.The same goes if you are using another pub/sub implementation that doesn't automatically perform the necessary cleanup when the $scope is destroyed, or if your directive passes callbacks to services.
Another situation would be to cancel
$interval
/$timeout
:If your directive attaches event handlers to elements for example outside the current view, you need to manually clean those up as well:
These were some examples of what to do when directives are "destroyed" by Angular, for example by
ng-view
orng-if
.If you have custom directives that manage the lifecycle of DOM elements etc. it will of course get more complex.