Thursday, January 22, 2015

How to determine the number of bindings in Angular


Angular Uses Bindings

Anyone using Angular should already know that.

Depending on the size of your app you might have many bindings. You may even have too many bindings. The magic number I keep reading about is around 2000 bindings for a given app. Anything above that tends to slow down processing of the app.

How many bindings you are using?

The simplest and least accurate method would be to count the number of bindings you see in your template and multiply it by the number of repeated objects you have on the page. Doing this you are likely to get a number that is a huge underestimation.

Instead you can take the code below and paste it into the console of your preferred web browser. You must have Underscore.js or Lo-Dash loaded.


function getScopeList(rs) {
    var scopeList = [];
    function traverseScope(s) {
        scopeList.push(s);
        if (s.$$nextSibling) {
            traverseScope(s.$$nextSibling);
        }
        if (s.$$childHead) {
            traverseScope(s.$$childHead);
        }
    }
    traverseScope(rs);
    return scopeList;
}

scopes = getScopeList(angular.element(document.querySelectorAll("[ng-app]")).scope());
total = _.uniq(_.flatten(scopes.map(function(s) { return s.$$watchers; }))).length;


This will give you the total number of scopes for ONE ng-app. For many web app that is all you will have.

But I have bootstrapped more than one ng-app...

Some pages have more than one ng-app. While I don't recommend this it, sometimes, needs to happen. If this is your case then you will want to paste the code below into the browser console:


function getScopeList(rs) {
  var scopeList = [];

  function traverseScope(s) {
    scopeList.push(s);
    if (s.$$nextSibling) {
      traverseScope(s.$$nextSibling);
    }
    if (s.$$childHead) {
      traverseScope(s.$$childHead);
    }
  }

  traverseScope(rs);
  return scopeList;
}

total = 0;
apps = angular.element(document.querySelectorAll("[ng-app]"));
[].forEach.call(apps, function(app,i) {
  ngapp = angular.element(app);
  slist = getScopeList(ngapp.scope());
  wl = slist.map(function(s) {return s.$$watchers;});
  c = _.uniq(_.flatten(wl)).length;
  appName = ngapp.attr("ng-app") || ngapp.attr("data-ng-app") || i;
  console.log("App %s: %d - Scopes: %O", appName, c, slist);
  total += c;
});
total


This will output a separate value for each ng-app that you have running and then the total for all ng-apps. The output also displays the name of the app by reading the attribute ng-app from the DOM element that defined it. And the last element displayed is the array of scopes that are part of that app.

The second set of code works for both single ng-apps or multiple ng-apps.

That's it

Now you can see how many bindings and scopes each of your ng-apps have.

Update - Feb 2, 2015
I apologize for the incorrect code examples above prior to Feb 2. I had taken my functioning code and, in an attempt to make the function names a little more explicit I broke the examples. Yes, I know, I should have tried them again before I posted, but I got lazy.

The code has been corrected and now works like it should.

No comments: