I've written code in my controllers to clean up resources on a $destroy event. It's straightforward to write unit tests to verify the operation of my cleanup method, but it's reasonable to wonder this: When will my app ever get a $destroy event? What can I do as a user in my browser to make my app get a $destroy event?
From the docs
Removes the current scope (and all of its children) from the parent
scope. Removal implies that calls to $digest() will no longer
propagate to the current scope and its children. Removal also implies
that the current scope is eligible for garbage collection.
The $destroy() is usually used by directives such as ngRepeat for
managing the unrolling of the loop.
Just before a scope is destroyed, a $destroy event is broadcasted on
this scope. Application code can register a $destroy event handler
that will give it a chance to perform any necessary cleanup.
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.
http://docs.angularjs.org/api/ng/type/$rootScope.Scope
It appears there are a couple of things related to $destroy. My basic understanding of this is that any time a scope is no longer needed navigating away from some view and now the controller/scope used for that view is no longer needed or some DOM element is removed $destroy is called. The places I've seen this actually in use are for cleaning up timers or listeners so that the memory can be garbage collected.
Related
This may not be a Mithril specific question but VM DOM diffing in general. While "redrawing" a page and you attach a new event handler, remove an event handler, or change the event handler associated with an event such as "onclick", it seems that the VM DOM diffing will not detect the change and I have to explicitly force a rerendering of the "real" DOM.
Is there a good pattern to handle these situations.
In Mithril specifically, onclick and all the on-handlers automatically trigger a redraw after the handler function has completed. However, if your onclick handler does real DOM manipulation (3rd party libraries such as jQuery or an animation suite) you need to use the config attribute:
http://lhorie.github.io/mithril/mithril.html#the-config-attribute
which gets called after the page has been rendered.
With regards to changing event handlers, it depends on when in the render cycle you make the change. I'd have to see some code. If it's not too inconvenient, calling redraw manually is not a bad thing. If the redraw strategy is "diff" you won't pay a penalty.
And lastly, if you are introducing your own events, consider using m.startComputation/m.endComputation in your event handlers. These provide much finer grain control and a redraw is automatically called.
http://lhorie.github.io/mithril/mithril.computation.html
What happens inside AngularJS when a event is fired with the Angular internal event system?
From a performance point of view, should I tend to use watches or events to update parts in my application?
When a JS event is fired, it is typically handled in the same way that all JS events are handled - there is nothing special or unique about this.
However, angular will wrap the handler inside of an $apply block so that after it executes the function, it can trigger a digest cycle:
$scope.$apply(function(){
$element.on('click',function(e){
...
});
})
A digest cycle iterates over all your scope variables, compares each one with the previous value to determine if anything has changed, and if it has, then their corresponding $watch handlers are called to update the view.
Since you are using angular, set up $watch expressions when you want to detect that a model on your scope has changed, and then do any DOM manipulations from inside your $watch handler. If you are concerned about performance, make sure your $watch function is optimized (i.e. avoid full jquery, avoid expensive query selectors, minimize DOM manipulation etc.)
To answer your question, you should use $watches to monitor changes to your models, and $watch functions to update your view.
I'm trying to realize a simple directive in AngularJS. In particular I want to develop a loader button that change its aspect when pressed, and I want to reuse it in all the page of my application that need it.
I have read on the developer guide that:
"There are a few special events that AngularJS emits. When a DOM node that has been compiled with Angular's compiler is destroyed, it emits a $destroy event. Similarly, when an AngularJS scope is destroyed, it broadcasts a $destroy event to listening scopes. 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."
In my link function I have put this code for the event listener:
var onLoaderButtonClickEvent = element.on('click', function(){
//Some stuff
});
Now, have I to consider that as a listener on a DOM element (and so I have to remove it) or not? I'm a lit bit confused.
I think that I have not to remove the listener because is on the "element". Is it correct?
Thx to all
The element.remove() is called automatically when a directive is destroyed, thus removing all listeners on the element. You only have to remove DOM listeners manually if you attached them to any other DOM elements.
From angular's documentation:
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.
The answer is yes. As you've attached an event handler outside of AngularJS, you'll need to clean it up yourself.
You can do this by listening to the $destroy event:
scope.$on('$destroy', function(){
element.off('click');
});
I have a directive which encapsulates a dialog box, and it's almost completely isolated from the surrounding scopes: the only way to communicate with/from it is by firing events. For instance, dialog box commands such as "Apply", "Save", "Delete" or "Turn the lights off" are issued by clicking buttons and firing corresponding events which other scopes may be waiting for.
As we don't know which scopes are registered to handle any specific events, I've resorted to broadcast the dialog's command events all the way up from $rootScope, and of course it works. However, if an event handler changes the model, the view isn't updated, because there's no $apply or $digest following the event firing.
Here lies my dilemma: where to call $digest from? Or am I very misguided about the whole thing?
Since $broadcast is synchronous, I suggest that the code that is calling $broadcast should call $rootScope.$apply() after calling $broadcast, if that code is running "outside" AngularJS. (If that code is running "inside" Angular, $apply() should not be needed.)
First all, I want to be clear that my question is NOT about the $destroy event associated with AngularJS Scope.
AngularJS patches jQuery to make all jQuery's DOM destruction apis to fire a $destroy event on all DOM nodes being removed. jqLite (comes with AngularJS) also behaves in the same way, if no jQuery is used. I have seen the $destroy event been used (ie. bind to) in a few occurrences in AngularJS' source. But still, I cannot reason what are some practical uses of binding to the $destroy event on DOM elements being removed. Please enlighten me.
Here is a valid issue from the AngularJS github repository that could be solved with this event: https://github.com/angular/angular.js/issues/3237
Check this fiddle: http://jsfiddle.net/Amh8W/2/
$element.on('$destroy', function() {
$scope.window.close();
});
Window is closed when directive is removed - try to handle that in another way. $destroy has functionalty similar to destructor.