Monthly Archives: August 2016

Mystery behind track by in ng-repeat

This is how AngularJS works under the covers (in a nutshell) when you use ng-repeat:

  1. It iterates over each item in the array (or each key, value in the object)
  2. It runs each item via any filters that are present in our expression, to check if it should be displayed or not
  3. It calculates a hash value by which it identifies the object (which is by reference by default)
  4. It checks if it has already created a DOM element for the hash value previously
    If so, it reuses it
  5. If not, it creates a DOM element based on the ng-repeat template
  6. All the DOM manipulations are taken and inserted in an optimal manner into the actual DOM
  7. A watch is added on the array, which triggers step 1 again if the array undergoes any change

$$hashkey is a auto generated key which angular assigns to array,collection passed in ng-repeat directive. Angular uses it to keep track of any changes made to array and update doom accordingly.

As we know ng-repeat is used to tight up collection to DOM. Suppose we have an collection of 20 elements and are using it create a complex DOM structure. Suppose we make changes to the array and add or edit elements, this would result in angularjs recreating entire dom structure for the new array. This can become a costly operation in many cases, causing lot of browser repaints. By passing “track by” to ng-repeat which is unique to the array, when array changes, angular only recreates dom only for the changed array value, and not the entire array. “track by” tells AngularJS that each item is uniquely identified by the property passed in the track by expression, and to reuse DOM elements as long as the that field does not change. This is a quick and simple step to optimizing ng-repeat in AngularJS, to ensure DOM manipulations in your application are reduced.

Consider following example

TrackBy-1

angular.module('myApp', [])
.controller('tasksCtrl', function($scope) {
	 
    function getTasks(){
    	var tasks = [];
    	for (var i = 1; i < 301; i++) {
         var task = {
            id: i,
            title: 'Task ' + i
         }
         task.latestDate = new Date().toISOString();
         tasks.push(task);
    	}
      return tasks;
    }
    $scope.tasks = getTasks();
       
    $scope.refresh = function() {
        console.log("refreshing")
        $scope.tasks = getTasks();
    };
})

 

As you can see when we click refresh we are assigning new tasks collection. It would cause ngRepeat to remove all li elements of existing tasks and create them again, which might be expensive (e.g. we have a lot of them or each li’s template is complex). That means a lot of DOM operations.

Behind the scenes ngRepeat adds a $$hashKey property to each task to keep track of it. If you replace the original tasks with new tasks objects from the server, even if those are in fact totally identical to your original tasks, they won’t have the $$hashKey property and so ngRepeat won’t know they represent the same elements.

TrackBy-2

You can change the above to be ng-repeat="task in tasks track by task.id" and since the ID would be the same in both your original tasks and the updated ones from the server – ngRepeat will know not to recreate the DOM elements and reuse them.

 

Mystery between $scope.apply(); and $scope.apply(function(){})

When you make a change outside of a non-Angular method, so a native DOM event or method in a third party library like jQuery, Angular does not know anything changed and as such, will not acknowledge anything has changed. All $scope.$apply does is basically says: “Hey Angular, a value changed, thought you should know, so you can tell your listener ($watchers) homies about it”.

Actions in AngularJS that trigger a $digest can include; in-built Angular directives like ng-repeat, ng-hide, ng-show, ng-click and even $http calls. If you try using $scope.$apply within one of these calls or even within a $timeout, you will get issues.
Classic scenario / example is updating scope in setTimeout function

setTimeout(function () {
    $scope.message = "Timeout called!";
    $scope.$apply();
}, 3000);

setTimeout(function () {
    $scope.$apply(function () {
        $scope.message = "Timeout called!";
    });
}, 3000);

$apply can be called in 2 ways

  • Without function
    $scope.$apply();
    
  • With function expression as parameter

    $scope.$apply(function(){
        
    });
    

So what’s the difference – The difference is that in the first version, we are updating the values outside the angular context so if that throws an error, Angular will never know. Now if you wrap inside the function call and that piece of code which updates scope throws exception, that exception will be handled by $exceptionHandler. Otherwise you will have to handle that exception yourself.

The only actually different between calling $apply(function() {}) with your code in the function body and calling $apply() after your code, is the exception handling.

Pseudo code for $apply with function

function $apply(expr) {
  try {
    return $eval(expr);
  } catch (e) {
    $exceptionHandler(e);
  } finally {
    $root.$digest();
  }
}

 

Scope’s $apply() method transitions through the following stages:

  1. The expression is executed using the $eval() method.
  2. Any exceptions from the execution of the expression are forwarded to the $exceptionHandler service.
  3. The watch listeners are fired immediately after the expression was executed using the $digest() method.

Mysteries around Compile, Link(Pre & Post) for angular directives

Compile: Compile function is to mutate the template before we link to it. It traverses the DOM and collect all of the directives and the result is a linking function. Compile function is executed before the link function. In compile function any manipulation we do for the DOM is only run once, irrespective of how many instances of directives are created. Compiled step is applied to all instances of photo.  Link function deals with specific instance of the directive. Compile returns link function. This return can be object of pre or post link function or just a simple function which by default is post link function. Difference between pre and post function is Pre we have access to element that is linked and not child elements which are not linked. In post we have access to element which is linked and to child elements which are linked as well. You cannot use compile and link function together, you have to use compile function which returns link function.

Link: combine the directives with a scope and produce a live view. Any changes in the scope model are reflected in the view, and any user interactions with the view are reflected in the scope model. Making the scope model a single source of truth. it is used for registering DOM listeners (i.e., $watch expressions on the instance scope) as well as instance DOM manipulation (i.e., manipulation of iElement = individual instance element).It is executed after the template has been cloned. E.g., inside an                              <li ng-repeat…>, the link function is executed after the <li> template (tElement) has been cloned (into an iElement) for that particular <li> element. A $watch() allows a directive to be notified of instance scope property changes (an instance scope is associated with each instance), which allows the directive to render an updated instance value to the DOM — by copying content from the instance scope into the DOM.

Below diagrams summarizes a bit

Compile-pre-post-angular-cycle

Most directives only need a link function, since most directives only deal with a specific DOM element instance (and its instance scope). One way to help determine which to use: consider that the compile function does not receive a scope argument. So the compile function can’t do anything you would want to do that requires an (instance) scope, you can’t $watch any model/instance scope properties, you can’t manipulate the DOM using instance scope information, you can’t call functions defined on the instance scope, etc. However, the compile function (like the link function) does have access to the attributes. So if your DOM manipulations don’t require the instance scope, you can use a compile function. Here’s an example of a directive that only uses a compile function, for those reasons. It examines the attributes, but it doesn’t need an instance scope to do its job. Another way to help determine which to use: if you don’t use the “element” parameter in the link function, then you probably don’t need a link function. If you create a directive that only has a link function, AngularJS treats the function as a post-link function.

In case of nested directives this is how the compile link (Pre and post) functions are executed, lets take an example of following 3 custom directives

In case of nested directives this is how the compile link (Pre and post) functions are executed, lets take an example of following 3 custom directives

Directives-level-1-2-3

3 Functions will be executed in this order

  • Compile – levelOne, levelTwo, levelThree
  • Pre – levelOne, levelTwo, levelThree
  • Post: levelThree, levelTwo,  levelOne

Notice how the order of the compile and pre-link functions calls is identical but the order of the post-link function calls is reversed. After Angular travels down the DOM and has run all the compile functions, it traverses back up again and runs all associated post-link functions. The DOM is now traversed in the opposite direction and thus the post-link functions are called in reverse order. So while the reversed order looked strange a few minutes ago, it is now starting to make perfect sense. This reverse order guarantees that the post-link functions of all child elements have run by the time the post-link function of the parent element is run. So when the post-link function of <level-one> is executed, we are guaranteed that the post-link function of <level-two> and the post-link function of <level-three> have already run. This is the exact reason why it is considered the safest and default place to add your directive logic.

When writing a post-link function, you are guaranteed that the post-link functions of all child elements have already been executed. In most cases that makes perfect sense and therefore it is the most often used place to write directive code. However, Angular provides an additional hook, the pre-link function, where you can run code before any of the child element’s post-link functions have run. The pre-link function is guaranteed to run on an element instance before any post-link function of its child elements has run. So while it made perfect sense for post-link functions to be called in reverse order, it now makes perfect sense to call all pre-link functions in the original order again. This also implies that a pre-link function of an element is run before any of its child elements pre-link functions as well, so for the sake of completeness – A pre-link function of an element is guaranteed to run before any pre-link or post-link function of any of its child elements.

angular-pre-post-compile

Mystery of Transclusion – Angular JS

Definition of transclusion – The inclusion of a document or part of a document into another document by reference. example are including styles.css in html page, or server side include which injects HTML fragment in a document.

In angular JS you typically custom fragment through directives and that’s where transclusion is important from angular perspective.

Two key features are provided by AngularJS to support transclusion. The first is a property that is used in directives named transclude. When a directive supports transclusion this property is set to true. The second is a directive named ng-transclude that is used to define where external content will be placed in a directive’s template. The directive code that follows uses these two AngularJS features to enable transclusion.

Consider a directive called custom-directive-element in an element, and that element is enclosing some other content, let’s say:

Transclusion-1

var myCustomApp = angular.module('myApp',[]);
myCustomApp.directive('customDirectiveElement', function(){
    return {
        template: 'This is my custom directive element content'
    }
});

This will result in rendering
Transclusion-2

Notice that the content of your original element will be lost (or better said, replaced), button and href are gone

<button>Submit</button>
<a href="#">Home</a>

So, what if you want to keep your button… and href.. in the DOM? You’ll need something called transclusion. The concept is pretty simple: Include the content from one place into another.

So with transclusion the directive should look like this

myCustomApp.directive('customDirectiveElement', function(){
    return{
               restrict: 'E',
               transclusion: true,
               template: 'This is my custom directive element content 
                                         
'
} });

This would render:

Transclusion-3

In conclusion, you basically use transclude when you want to preserve the contents of an element when you’re using a directive.

ES6 let keyword

JavaScript ES6: the let keyword. let enables a new form of scoping. var is scoped to the nearest function block and let is scoped to the nearest enclosing block (both are global if outside any block), which can be smaller than a function block. Also, variables declared with let are not visible before they are declared in their enclosing block unlike var type in which case the variables get hoisted at the top of the function. For hoisting refer previous blogs if required. In its most basic form, let is a sibling to var. But declarations made with let are scoped to the blocks in which they occur, rather than being “hoisted” to the enclosing function’s scope as vars do

The let statement declares a block scope local variable, optionally initializing it to a value.

let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.

Scoping rules

Variables declared by let have as their scope the block in which they are defined, as well as in any contained sub-blocks . In this way, let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function:

function oldES5varTest() {
    var x = 100;
    if (true) {
        var x = 200;  // same variable!
        console.log(x);  // 200
    }
    console.log(x);  // 200
}

function newES6letTest() {
    let x = 100;
    if (true) {
        let x = 200;  // different variable
        console.log(x);  // 200
    }
    console.log(x);  // 100
}

 

It makes inner function code much cleaner

var list = document.getElementById("list");

for (let i = 1; i <= 5; i++) {
    let item = document.createElement("li");
    item.appendChild(document.createTextNode("Item " + i));

    item.onclick = function (ev) {
        console.log("Item " + i + " is clicked.");
    };
    list.appendChild(item);
}

The example above works as intended because the five instances of the (anonymous) inner function refer to five different instances of the variable i. Note that it does not work as intended if you replace let with var, since all of the inner functions would then return the same final value of i: 6. Also, we can keep the scope around the loop cleaner by moving the code that creates the new elements into the scope of each loop.

At the top level of programs and functions, let, unlike var, does not create a property on the global object. For example:

var x = 'globalX';
let y = 'globalY';
console.log(this.x); // "globalX"
console.log(this.y); // undefined

 

Block scoping simply says, I want to be able to treat a { .. } block as a scope, without having to make a new function to encapsulate that scope.Other languages (e.g., Java, C++) have true block scoping, where you can declare a variable to belong to a specific block instead of to the surrounding scope/function.

Angular 2 – *ngFor – What’s the magic behind * (asterisk) and other mysteries..

In angular 1.* we used to use ng-repeat pretty heavily like

<ul>
    <li ng-repeat="item in items">
        {{item.name}}
    </li>
</ul>

 

Now with angular 2 we achieve same thing with with ngFor

<ul>
    <li *ngFor="let item of items">
        {{item.name}}
    </li>
</ul>

 

The let key is part of the Angular 2 template syntax. let creates a local variable that can be referenced anywhere in our template. So in our case we are creating a local variable let item.

What is this * for?
As you can see there’s no more ng-repeat, it’s ngFor now. why the asterisk? The answer to that is, it’s syntactic sugar. ngFor can only be applied to a <template>. *ngFor is the short form that can be applied to any element and the <template> element is created implicitly behind the scene.  The *  makes it easier to read and write directives that modify HTML layout with the help of templates. NgFor, NgIf, and NgSwitch all add and remove element subtrees that are wrapped in<template> tags.

The HTML template element <template> is a mechanism for holding client-side content that is not to be rendered when a page is loaded but may subsequently be instantiated during run time using JavaScript. Think of a template as a content fragment that is being stored for subsequent use in the document.

*ngFor undergoes a transformation like shown below

<ul>
    <li *ngFor="let item of items;" [item]="item"></li>
</ul>

 

Here’s the same after transporting the ngFor to the template directive:

<ul>
    <li template="ngFor let item of items;" [item]="item"></li>
</ul>

And here it is expanded further into a tag wrapping the original element:

 

<ul>
    <template ngFor let-item [ngForOf]="items">
        <li [item]="item">{{item.name}}</li>
    </template>
</ul>

When Angular sees the asterisk (*) in ngFor, it will use its DOM element as template to render the loop.

Moreover *ngFor has more properties i.e index, first, last , even, odd we can get the value of index per iteration, can get the last value or odd, even index too using local variables. here is working example for the same:

<ul>
  <li *ngFor='let item of items #i=index #l=last #e=even '>
    Item value is : {{item}} has index value is : {{i}} and last value is :{{l}} even index :{{e}}</li>
</ul>

 

NgFor provides several exported values that can be aliased to local variables:

  • index will be set to the current loop iteration for each template context.
  • first will be set to a boolean value indicating whether the item is the first one in the iteration.
  • last will be set to a boolean value indicating whether the item is the last one in the iteration.
  • even will be set to a boolean value indicating whether this item has an even index.
  • odd will be set to a boolean value indicating whether this item has an odd index.

Angular 2 also includes the trackBy feature from Angular 1.x that allows performance improvements in our list rendering by tracking a unique identifier on our list items.

<ul>
    <li *ngFor="let item of item; trackBy:item.id;">
        {{$index}} {{item}}
    </li>
</ul>