So you ended up with a wild, fast and strong dog! but you need to put a leash on it or it WILL bite you!

Angularjs : One Way binding

“Angular is shipped with magical powers which make our dreams come true”.
Probably you’ve heard this sentence over and over since Angular was released. Though it is a truthy sentence because angular is shipped with two-way binding, Dependency injection and MVC structure ...etc.

So you ended up with a wild, fast and strong dog! but you need to put a leash on it or it WILL bite you!


Why one way binding?

First off, what is two-way binding? simple it a declaration interpreted by angular in order to get evaluated within the template.

//controller.js
$scope.title = "This is title!"
//index.html
<h1>{{ title }}</h1>

Simple enough?

Essentially angular uses algorithm called dirty checking to keep track of specific values on the scope.

Every time it got changed in the model, it will run a digest cycle to update all the values bounded by the scope.
So when you use the double braces {{}} you are telling angular to watch whatever expression inside this tags, whenever it changes in the scope, it take effect on the model instantly.

Where is the problem?

With great powers comes great responsibility, this fancy service doesn’t come without a price! it will create a dedicated watcher to monitor this value, still what if you overused it ?! basically you are gonna end up with the same case we had to deal with at Namshi, we had an internal app that has multiple nested views, ng-repeats and a plenty of conditional bindings.

The performance was painfully slow! and one of the numerous reasons was the two-way binding!

To give you an idea.. running this inside the console will give us the watchers count on this page.

(function () { 
var root = angular.element(document.getElementsByTagName('body'));

var watchers = [];

var f = function (element) {
angular.forEach(['$scope', '$isolateScope'], function (scopeProperty) {
if (element.data() && element.data().hasOwnProperty(scopeProperty)) {
angular.forEach(element.data()[scopeProperty].$$watchers, function (watcher) {
watchers.push(watcher);
});
}
});

angular.forEach(element.children(), function (childElement) {
f(angular.element(childElement));
});
};

f(root);

// Remove duplicate watchers
var watchersWithoutDuplicates = [];
angular.forEach(watchers, function(item) {
if(watchersWithoutDuplicates.indexOf(item) < 0) {
watchersWithoutDuplicates.push(item);
}
});

console.log(watchersWithoutDuplicates.length);
})();
// Credits :http://stackoverflow.com/questions/18499909/how-to-count-total-number-of-watches-on-a-page

To give you more sense of what to expect 2000 watchers is pretty jammed and on the limit of a good healthy angular page.
Well, in our page we had more than 24,000 watcher :)

Generally, this loophole is fixed by adding “ ::” in every {{}} (introduced in angular 1.4) which allows angular to interpret this value only once and leave it and it won’t get evaluated on the next digest cycle.

{{:: title}} //only evaluated once

This pretty straight forward, right? But there is a trick :) 
You need to be very meticulous while using this feature as it might run unexpectedly.
for different reasons the value might not be available while rendering it in the template so you will end up with a screwed up view.
another case challenged us

{{:: getValueFromMethod(param1, param2) | filter:param3 }}

On the previous example we had issues sometimes rendering this expression for different reasons:

  1. The function is called fine but the in a time that the data on the other end wasn’t ready so returned defined and so, it rendered Nothing.
  2. Function pulls the data but when it come the passing it to the filter which is might has a glitchy performance we will end up with the same result.

So in this case, it might be a good idea to leave the two-way binding ON as the data become available to the view through such complex expressions gets rendered.

References :

Dirty Checking in angular: 
https://docs.angularjs.org/guide/scope#scope-life-cycle
http://ryanclark.me/how-angularjs-implements-dirty-checking/

One time binding Doc:
 https://docs.angularjs.org/guide/expression#one-time-binding