Skip to content

Instantly share code, notes, and snippets.

@DTFagus
Created August 12, 2014 14:22
Show Gist options
  • Star 41 You must be signed in to star a gist
  • Fork 16 You must be signed in to fork a gist
  • Save DTFagus/3966db108a578f2eb00d to your computer and use it in GitHub Desktop.
Save DTFagus/3966db108a578f2eb00d to your computer and use it in GitHub Desktop.
Bookmarklet to analyse angular watchers

Copy as link into bookmark. Outputs into console.

javascript: (function() {
var root = $(document.getElementsByTagName('html'));
var watchers = [];
var attributes = [];
var attributes_with_values = [];
var elements = [];
var elements_per_attr = [];
var scopes = [];
function include(arr, obj) {
return (arr.indexOf(obj) != -1);
}
function is_not_duplicate(arr, obj) {
if (typeof arr == "undefined") {
return true;
} else {
if (include(arr, obj)) {
return false;
} else {
return true;
}
}
}
var f = function(element) {
if (element.data().hasOwnProperty('$scope')) {
if (typeof scopes[element.data().$scope.$id] == "undefined") {
scopes[element.data().$scope.$id] = true;
angular.forEach(element.data().$scope.$$watchers, function(watcher) {
watchers.push(watcher);
for (index = 0; index < element[0]['attributes'].length; ++index) {
if (is_not_duplicate(elements_per_attr[element[0]['attributes'][index].nodeName], element.data().$scope.$id)) {
if (typeof elements[element[0]['attributes'][index].nodeName] == "undefined") {
elements[element[0]['attributes'][index].nodeName] = [];
}
elements[element[0]['attributes'][index].nodeName].push({
element: "(Scope " + element.data().$scope.$id + "): " + element[0].outerHTML,
element_obj: element[0],
element_data: element.data(),
relevant_watcher: watcher,
current_value: watcher.last
}
);
if (typeof elements_per_attr[element[0]['attributes'][index].nodeName] == "undefined") {
elements_per_attr[element[0]['attributes'][index].nodeName] = [];
}
elements_per_attr[element[0]['attributes'][index].nodeName].push(element.data().$scope.$id);
}
}
});
}
}
angular.forEach(element.children(), function(childElement) {
f($(childElement));
});
};
f(root);
console.log("####################");
console.log("Analysing Watchers");
console.log("####################");
console.log(" ");
console.log("Watchers:");
console.log(watchers.length);
console.log("----------------------------------------------");
console.log("Watched Elements grouped by attribute");
console.log(elements);
})();
@ti2m
Copy link

ti2m commented Oct 24, 2014

Replacing the two usages of $() with angular.element() makes it jquery independent. Really useful, thanks!

@skusunam
Copy link

skusunam commented Nov 2, 2014

Thanks for the gist.
It doesn't count watchers on isolate scope's. I guess this makes lot of difference where apps are component based.

element.scope() will never return the isolate scope anymore.

isolateScope() - retrieves an isolate scope if one is attached directly to the current element. This getter should be used only on elements that contain a directive which starts a new isolate scope. Calling scope() on this element always returns the original non-isolate scope.

@corporatepiyush
Copy link

Awesome. It helped me.

@idanen
Copy link

idanen commented Aug 27, 2015

Notice the index in the for loop leaks to global context.

@HadrienPierart
Copy link

Great bookmarklet, thx !
Indeed however, the count is off by a large amount in component based applications. I just tried it on a page of an app Im working on, and the bklet misses a good third of the watchers given by :

angular.element('html').scope().$$watchersCount

Given that this line returns something like 900 watchers on an heavy page, yours gives me ~600 !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment