When does a scope naturally get destroyed in angularjs lifecycle - javascript

I see this type of code a lot in angular modules
scope.$on('$destroy', function(){
//undind listener here
});
My understanding is that whenever the scope is about to be destroyed it broadcasts a $destroy event allowing you to clean up any code that may continue to run after the destruction of the scope which would create memory leaks.
My question is, when does the scope naturally get destroyed in an angularjs app. All the documentation I can find from the website is that you can manually call $destroy to remove a scope, but this seems to suggest that it will happen at some point automatically. When would that be?

Scope is tied to HTML elements during compilation. $compile needs a scope to compile an element. Elements could be nested. Some get new scope other inherit.
Scope gets destroyed when elements are removed from DOM.
To be precise: A $destroy handlers are called on jQuery.cleanData which AngularJS redefines and calls after it does its cleanup - aka acting in destroying the scope.
cleanData function is called when an element is removed from the DOM.
What is the purpose of jQuery clean and cleanData methods?

Related

Angular directive disposal

I'm trying to figure out what would be the best way to perform cleanup on different angular directives.
I have different types of directives, some do not define their own scope, some have an isolated scope, and some have a child scope.
I need a generic mechanism that will take care of the cleanup in a separate component that my directives use.
So basically I'm looking at two different options, either register on angular's element.on('$destroy', function() {...}) or on jquery's scope.$on('$destroy', function () {...}).
Here's the problem:
If I register on the underlying element destruction then I miss destruction of directives that they're element was not destructed (not sure exactly how's that possible, noticed it via testing...).
If I register on the underlying scope destruction then (I think) I miss destruction of directives that they're element was destructed, for instance when the directive is not defining its own scope and is using its parent scope.
Looking at angular's directives documentation I came across this:
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.
There's no mention as far as I can tell about which strategy to use when.
Also, looking at Angular's documentation I came across this:
When child scopes are no longer needed, it is the responsibility of the child scope creator to destroy them via scope.$destroy() API.
I don't understand the scenario in which I am supposed to call the scope.$destroy() API on my own.

Trigger $digest from external code

I have an application that exposes an methods (from various angular services) to code that is loaded and eval'd at runtime. What is the best way to guarantee $digest triggers when these methods are called from outside angular, taking into account that they might also be called from within angular? Should I expose a separate interface wrapped in $scope.$apply instead of exposing the service methods directly?
Quick solution would be to call $evalAsync. From documentation:
" if this function is called outside of a $digest cycle, a new $digest cycle will be scheduled. " https://docs.angularjs.org/api/ng/type/$rootScope.Scope
My advice to expose something outside Angular is to expose element (by providing ID or selector) where you have scope or (better) controller attached and then use 'angular.element(someDomEkenent).scope().someFunction()'
In this case you would avoid possibility to capture unessesary objects that can lead to memory leaks that is very hard to debug
call $timeout() function with no time.
for example
function someEventOutsideAngualer(value) {
$timeout(function(){
$scope.bindedValue = value;
})
}
The idea is that when $timeout callback is executed, in our case it will be picked up with the next $digest. Please do not use $scope.$apply() and $digest triggers. They cause more problems than they solve, if not properly understood

eslint warning interpretation

I've been playing around a bit with ES6 and angular and I'm using eslint-plugin-angular to validate my javascript. I have the following service:
export function runBlock ($rootScope, $state, $log) {
'ngInject';
$rootScope.$on( '$stateChangeStart', function(event, toState) {
// ...
} );
But eslint gives me the following error:
The "$on" call should be assigned to a variable, in order to be
destroyed during the $destroy event
I mean I understand the warning, but I've never done this in my previous angular projects, should I have done what the error suggests? Why is it needed/good practice?
The docs for eslint-plugin-angular reference John Papa's angular styleguide, but I didn't really find a mention of this situation there.
Not only does the johnpapa styleguide not mention this situation, it actually includes an example of ignoring the return of $rootScope.$on. However, the discussion on one of the eslint-plugin-angular issues clarifies the intent a little bit:
If a controller is registering a listener on $rootScope it should probably be manually destroyed in "$destroy" since root scope will outlive all the controllers. --davidmason
That post also indirectly references the "Directives should clean up after themselves" best practice from the AngularJS documentation.
So bottom line: A regular $scope object will eventually be destroyed when its controller does, and take its event listeners with it (assuming you haven't done any kind of circular referencing that keeps it in scope). $rootScope never dies, and therefore never releases its event handlers. If your controller is adding an event listener to $rootScope, it should remove that handler in your controller's $destroy handler.

AngularJS - Clear all $watch on $destroy

I have a few $watch on my scope. I know that I a $watch will return its deregister function, and I can just call it to deregister it, but is there a way to just deregister all watchers on a scope in one simple command?
I want to basically deregister all of my watchers on $destroy, and I don't want to save ALL of these watchers, and call them all one by one.
There is a $$watcher attribute on the scope. Can I somehow use that?
Basically when $destroy is broadcasted from the scope, it also means that the scope has invoked the $destroy() method. This means that everything related to that scope is also being cleaned up, including the watchers, this part of the source code shows how the $destroy() method empties the watchers of that scope.

Ensuring no memory leaks in backbonejs app

I have a backbonejs app that works something like this:
[Backbone MODEL] <----> [Backbone VIEW] <----> [DOM ELEMENT]
The model and view are created and assigned to local variables within a function scope. If I remove the dom element (by calling jQuery().empty() on it's parent element), will this delete all references to the View & subsequently the Model to avoid zombie objects/memory leaks?
As long as you are never assigning the model or view variables to anything that will stick around. Be wary of closures around the variables, don't attach the variables as attributes of any other object, don't attach the view or model to any events from any other objects. Then yes, this will clean itself up.
But those are some pretty strict rules to abide by and don't play well with most apps. You should be handling the removal of event handlers explicitly, and deleting any attribute on any object you had created.
I have a few articles related to this (seems likely you've read at least one of them, already):
The classic Zombies post: http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/
an implementation of the zombie's cleanup code, plus more, as an object on it's own: http://lostechies.com/derickbailey/2011/12/12/composite-js-apps-regions-and-region-managers/
Memory management in JavaScript and Backbone: http://lostechies.com/derickbailey/2012/03/19/backbone-js-and-javascript-garbage-collection/

Categories

Resources