Angular is a convenient but complex framework that makes it easy to connect your model to your view using controllers. However, as your app grows, the number of controllers in it increases, and it's easy to introduce leaks by not cleaning up after your controllers. In this article, we'll discuss how to avoid this problem by using the $destroy event.
When a controller is destroyed, it emits a $destroy event that you can listen to inside your controller. This is the point in your controller where you can delete data that might otherwise stay bound (bound data won't be reclaimed by the garbage collector), clean up DOM event listeners, etc.
Luckily, when a controller is destroyed it will emit the ++code>$destroy++/code> event, which you can listen to inside your controller by using:
++pre>++code>myApp.controller('myController', function($scope) {
... // Your controller code goes here
$scope.$on('$destroy', function() {
// Do your clean up
});
++/code>++/pre>
This is the point in your controller where you can delete data that might otherwise stay binded (binded data won't be reclaimed by the garbage collector), clean up DOM event listeners, etc.
So, now that you know what ++code>$destroy++/code> is all about, let's try it out.
I set up a code pen where you can create or destroy a controller by clicking in the button ++code>Click to toggle controller++/code>. This creates a new controller that listens for a ++code>dispatched++/code> event and a button that emits said event. The listener logs when the controller was instantiated.
Since we are using ++code>$scope.$emit++/code> and ++code>$scope.$on++/code> the controller cleans up after itself. To verify this, do the following:
This will give you two different dates for the two times the controller was instantiated.
Unfortunately, these functions only work for events dispatched in angular, so if you had an event emitted using ++code>element.dispatchEvent++/code> from another library (a chart, a react component; etc.) you couldn't use ++code>$scope.$on++/code> to listen to it.
A first approach would be to replace the ++code>$scope.$on++/code>, with a ++code>document.addEventListener++/code>. This, however, will create a new listener in the document element every time you create a new controller.
If you test the buttons now, you will see that we log once for every controller that was instantiated, so even though the controller was destroyed, the eventListener attached to the document element is still there.
To fix this, enter the ++code>$destroy++/code> event. All you have to do is listen to it using ++code>$scope.$on++/code> and use ++code>document.removeEventListener++/code> to remove the listener. Just add this to your controller:
++pre>++code>$scope.$on('$destroy', function() {
console.log('clean up');
// Remove the listener
document.removeEventListener('dispatched', listener);
});
++/code>++/pre>
If you now try the buttons, it should all work as expected.
By using the $destroy event, you can avoid memory leaks in your Angular controllers. It's a simple but effective way to ensure that your app stays responsive and efficient.