Practical Question Is do you really need to call clearWatcher being that
watcher bound to the $scope of the directive that will be destroyed
when the directive element will be removed from the DOM?
No, you do not need to call the watcher functions, angular will do that cleanup for you when the scope is destroyed.
see this link: angular.js $destroy event - should I manually unbind?
Note: although those watchers will be unbound when the scope is destroyed, this will only apply to the current scopes $destroy. You have listeners on the $rootScope here
var clearRoot1 = $rootScope.$on('event1', doSomething);
var clearRoot2 = $rootScope.$on('event2', doSomethingElse);
These will not be unbound, because destroying this controllers $scope will not call $destroy() on $rootScope. In this case, given that you want those listeners to be unbound when this scope is destroyed, then you do need to invoke those watchers. That brings me to your second question.
Aesthetical Question Is there a nice way to clear all the listeners
with only one callback on $destroy event? Something like
$scope.$on('$destroy', ['clear1', 'clear2']);
Not in that fashion. listening to '$destroy' via the $scope.$on method is the same as listening to any other event, see the documentation for $on
$on(name, listener); Listens on events of a given type. See $emit for
discussion of event life cycle.
The event listener function format is: function(event, args...). The
event object passed into the listener has the following attributes:
targetScope - {Scope}: the scope on which the event was $emit-ed or
$broadcast-ed.
currentScope - {Scope}: the scope that is currently
handling the event. Once the event propagates through the scope
hierarchy, this property is set to null.
name - {string}: name of the
event.
stopPropagation - {function=}: calling stopPropagation function
will cancel further event propagation (available only for events that
were $emit-ed).
preventDefault - {function}: calling preventDefault
sets defaultPrevented flag to true.
defaultPrevented - {boolean}: true
if preventDefault was called.
You could register a single function to the destroy event, and invoke the watchers from the called function. You could store them locally when they're registered, or just invoke them immediately. This is your example updated to reflect the first of these options.
(function() {
'use strict';
angular
.module('app')
.controller('myController', myController);
myController.$inject = ['$rootScope'];
function myController($rootScope) {
//my app code...
$scope.$on('$destroy', function() { cleanup(); });
function initialize() {
$scope.rootWatchers = [];
$scope.rootWatchers.push( $rootScope.$on('event1', doSomething) );
$scope.rootWatchers.push( $rootScope.$on('event2', doSomethingElse) );
$scope.$watch('model', updateSomething);
}
function cleanup() {
angular.forEach( $scope.rootWatchers, function(stopWatcher) { stopWatcher(); } );
}
}
})();