Thursday, February 18, 2016

AngularJS Data Binding



https://thecodebarbarian.wordpress.com/2014/01/31/what-you-need-to-know-about-angularjs-data-binding/
data binding consists of a set of functions associated with a scope. A scope is an execution context for the expressions you write in your HTML. AngularJS scopes behave like scopes in Javascript: a scope contains a set of named variables and is organized in a tree structure, so expressions in a given scope can access variables from an ancestor scope in the tree. However, data binding adds three powerful functions to a scope that enable you to assign an event handler to fire when a variable in scope changes as easily as you assign an event handler to fire when a button is clicked.

This function takes an expression and a callback: the callback will be called when the value of the expression changes. For example, lets say our scope has a variable name, and we want to update the firstName and lastName variables every time name changes. With $watch, this is trivial:
1
2
3
4
5
6
7
8
9
10
$scope.$watch('name', function(value) {
  var firstSpace = (value || "").indexOf(' ');
  if (firstSpace == -1) {
    $scope.firstName = value;
    $scope.lastName = "";
  } else {
    $scope.firstName = value.substr(0, firstSpace);
    $scope.lastName = value.substr(firstSpace + 1);
  }
});
Under the hood, each scope has a list of watchers, internally called $scope.$$watchers, which contain the expression and the callback function. The $watch simply adds a new watcher to the $$watchers array, which AngularJS loops over when it thinks something that can change the state of the scope.
When called without arguments, $apply lets AngularJS know that something happened that may have changed the state of the scope, so AngularJS knows to run through its watchers. You usually don’t have to call $apply() yourself, because directives like ngClick do it for you. However, if you’re writing your own event handler, like the swipeLeft and swipeRight directives from my guide to directives, you need to plug $apply() into your event handler.

At a high level, $digest() runs through every watcher in the scope, evaluates the expression, and checks if the value of the expression has changed. If the value has changed, AngularJS calls the change callback with the new value and the old value. 
2) The second subtlety is another interesting corner case with the change callback: what if the change callback calls $apply() or $digest()? Internally, AngularJS uses a system of phases to make sure this doesn’t happen: an error gets thrown if you try to enter the $digest phase while you’re already in the $digest phase.
3) Remember when we said that AngularJS scopes are organized in a tree structure and a scope can access its ancestor’s variables? Well, this means that $digest() needs to happen on every child scope in every iteration! Internally, this code is a bit messy in AngularJS, but each iteration of the $digest() loop does a depth-first search and performs the watcher check on every child scope. If any child scope is dirty, the loop has to run again!
4) Since Javascript is a single-threaded event-driven language, the $digest() loop cannot be interrupted. That means that the UI is blocked while $digest() is running, which means two very bad things happen when your $digest() is slow: your UI does not get updated until$digest() is done running, and your UI will not respond to user input, e.g. typing in an input field, until $digest() is done. To avoid a bad case of client side in-$digest-ion, make sure your event handlers lightweight and fast.
5) The final and often most overlooked subtlety is the question of what we mean when we say “checks if the value of the expression has changed”. Thankfully, AngularJS is a bit smarter than just using Javascript’s === operator: if AngularJS just used ===, data binding against an array would be very frustrating. Two arrays with the same elements could be considered different! Internally, AngularJS uses its own equals function. This function considers two objects to be equal if === says they’re equal, if angular.equals returns true for all their properties, if isNaN is true for both objects, or if the objects are both regular expressions and their string representations are equal. Long story short, this does the right thing in most situations:
1
2
3
4
5
6
7
angular.equals({ a : 1 }, { a : 1 }); //true
angular.equals({ a : 1 }, { a : 1, b : 2 }); // false
 
angular.equals([1], [1]); // true
angular.equals([1], [1, 2]); // false
 
angular.equals(parseInt("ABC", 10), parseInt("ABC", 10)); // true
TODO: 
https://thecodebarbarian.wordpress.com/2013/09/23/the-8020-guide-to-writing-angularjs-directives/
angular.
  module('myApp', []).
  directive('myBackgroundImage', function () {
    return function (scope, element, attrs) {
      element.css({
        'background-image': 'url(' + attrs.myBackgroundImage + ')',
        'background-size': 'cover',
        'background-repeat': 'no-repeat',
        'background-position': 'center center'
      });
    };
  });
Basic Directive Design Pattern: Watch and Update
        <div ng-controller="CarouselController" ng-init='setImages(["http://images2.wikia.nocookie.net/__cb20110811172434/fallingskies/images/f/fd/Totoro_normal.gif","http://images.wikia.com/pokemon/images/archive/4/49/20110526012846!Ash_Pikachu.png","http://images2.wikia.nocookie.net/__cb20111231185621/trigun/images/2/2b/Vash1.jpg"])'>
angular.
    module('myApp', []).
    directive('myBackgroundImage', function () {
        return function (scope, element, attrs) {
            scope.$watch(attrs.myBackgroundImage, function(v) {
                element.css({
                    'background-image': 'url(' + v + ')',
                    'background-size': 'cover',
                    'background-repeat': 'no-repeat',
                    'background-position': 'center center'
                });
            });
        };

    });
Basic Directive Design Pattern: Wiring External Event Handlers to call $apply
1. An $apply function that notifies AngularJS when some event has happened that may require updating the view.
2. An $eval function, which does a safe eval on its parameter in the scope.
<div my-background-image='image' class="tall" swipe-left="nextImage()"></div>
.directive('swipeLeft', function() {
        return function(scope, element, attrs) {
            $(document).ready(function() {
                Hammer(element).on('swipeleft', function() {
                    scope.$eval(attrs.swipeLeft);
                    scope.$apply();
                });
            });
        };
    })

http://stackoverflow.com/questions/9682092/how-does-data-binding-work-in-angularjs
AngularJS remembers the value and compares it to a previous value. This is basic dirty-checking. If there is a change in value, then it fires the change event.
The $apply() method, which is what you call when you are transitioning from a non-AngularJS world into an AngularJS world, calls $digest(). A digest is just plain old dirty-checking. It works on all browsers and is totally predictable.
To contrast dirty-checking (AngularJS) vs change listeners (KnockoutJS and Backbone.js): While dirty-checking may seem simple, and even inefficient (I will address that later), it turns out that it is semantically correct all the time, while change listeners have lots of weird corner cases and need things like dependency tracking to make it more semantically correct.

Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts