Sunday 13 March 2016

link function in Angular JS Directive

In this post i am going to discuss about the concepts of link function in angular Directive , normally the logic of directive is resides in the link function, which have the following parameters scope,element,attrs,controller , Now we see the example of the custom directive 

link:function Linkfn(scope,elem,attrs,ctrl){}

scope: the scope of the directive
elem: Dom element where the directive applied
attrs: collection of attrs
ctrl : array of controllers


Directive :
*************************



Controller:
*************************

appRoot.controller('MainController', function ($scope) {
    'use strict';   
     $scope.Product = {};
    
     $scope.sourceList = [{"id":1,"name":"Apple"},
                          {"id":2,"name":"Orange"},
                          {"id":3,"name":"Banana"},
                          {"id":4,"name":"Papaya"},
                          {"id":5,"name":"Jackfruit"}];
    $scope.Product.Selected = $scope.sourceList[0];
    
});



Html:
*************
Use the track-change directive in tag that you implement the ng-model, in this example i am used in select , where whenever user selects the new value, old value is retained in OldValue property, you can see the Model that maintains the history, but in view when we bind it shows only the latest value ,This is because of $formatters.






Output:
*******************



appRoot.directive('trackChange',function(){
    return{
        restrict:'A',
        require:'ngModel',
        link:function(scope,element,attrs,ngModel){
            
            var historyObject = {NewValue:undefined,OldValue:undefined};
            var OldValue = {};
            var NewValue = {};
            ngModel.$formatters.push(function(value){
                if(historyObject.NewValue!=undefined)
                    return historyObject.NewValue;
                if(value!=undefined){
                    historyObject.NewValue = value;
                }
                return value;
            });
            
            ngModel.$parsers.push(function(value){             
                
                historyObject.OldValue = historyObject.NewValue;;
                historyObject.NewValue =  value;
                ngModel.$setViewValue(historyObject.NewValue);
                ngModel.$render();
                ngModel.$setValidity('is_valid',true);
                return historyObject;
            });
            
        }
    }
})
var appRoot = angular.module('appRoot',[]);
appRoot.controller('MainController', function ($scope) {
    'use strict';   
     $scope.Product = {};
    
     $scope.sourceList = [{"id":1,"name":"Apple"},
                          {"id":2,"name":"Orange"},
                          {"id":3,"name":"Banana"},
                          {"id":4,"name":"Papaya"},
                          {"id":5,"name":"Jackfruit"}];
    $scope.Product.Selected = $scope.sourceList[0];
    
});
<div ng-app="appRoot">
    <div style="margin-left:40px;" ng-controller="MainController">
      
        <br />
        <select ng-change="modelChange()" track-change="" ng-model="Product.Selected" 
                ng-options="prod.name for prod in sourceList" >
        </select>  
        <span style="color:orange">[[Product.Selected]]</span>
</div>
</div>
From this post you can learn how to create a Directive with link function.

real usage of Compile function and order of execution in compile,link functions in angular js directive

In this post we are going to see the usage of compile function in angular js Directives,  while creating custom directive we many notice that most of the time logic resides in link function, but there is another function which is named as "compile"
In directive we have multiple functions, there is a logic difference between the functions, first is order of execution, second is for what kind of purpose this belongs to.

in General Compile Function executes first, then Pre link function finally post link function will executes, if a series of nested directives there then the order of execution will be

Directive 1: Dir1
Directive 2: Dir2
Directive 3: Dir3

<Dir1>
    <Dir2>
        <Dir3></Dir3>
    </Dir2>
<Dir1>

Compile function (Dir1) --> PreLink function(Dir1)
     Compile function(Dir2) --> PreLink function(Dir2)
       Compile function (Dir3)--> PreLink function(Dir3)
        PostLink function(Dir3)
  PostLink function(Dir2)
PostLink function(Dir1)


HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="application/javascript" src="angular.min.js"></script>
    <script type="application/javascript" src="mod.js"></script>
</head>
<body ng-app="app">
<div ng-controller="mainController">
    <compile-track>
        <degree></degree>
    </compile-track>
</div>
</body>
</html>


so the Compile function and PreLink function are given preference to the Top down approach in nested directives when it is executed, PostLink function are like bottom up approach always last element in the nested directive will execute first and the PostLink function of first element will execute last.


var app = angular.module('app',[]);

app.controller('mainController',['$scope',function($scope){

}]);

Now Let we see some sample, With compile function, Link function:


app.directive('compileTrack',function($compile){
return{
    restrict:'E',
    template:'<div>{{Title}}</div><br/>',
    compile: function (templateelement, attrs) {

        templateelement.append('<span style="font-weight: bold">Author :{{Author}}
        </span><span  style="font-weight: bold">, {{Purpose}}</span>');
return function (scope, instanceelement, attrs) { scope.Author = "Rajesh"; scope.Purpose = 'Architect'; scope.Title = "Angular Sample"; scope.technology = "Core"; var funcTemplate = '<br/><div>Technology : {{technology}}</div>'; instanceelement.append(funcTemplate); console.log('compileTrack : Post Link function'); } } } });


Order of execution of functions:



Output:


Template inside the functions:


From the above code , you can see the template are appended in the two function, one is inside the Compile function, another is inside the link function.But in the output screen, we can see the tempalte append inside the compile function is gives the correct output , but template append inside the link function is not replace with data, why it is so ? because the element passed inside the compile function is just the original template, but the element passed inside the link function is an instance of template,that means it is compiled one, so when ever you add the html or change the template in compile function, it will reflects in UI, but if you change anything inside the compile function then we have to append the template after compiling the code, then only it will bind with data, for example see the below image which will show the element passed as input parameters in both functions.


in compile function it is passed as templateelement, in link function it is passed as instanceelement, that means you can see the difference in template above.

Now we see example of how to change the template inside the link function., here we have to compile and inject.


var compileTemplate = $compile(funcTemplate)(scope);
instanceelement.append(compileTemplate);



app.directive('compileTrack',function($compile){
return{
    restrict:'E',
    template:'<div>{{Title}}</div><br/>',
    compile: function (templateelement, attrs) {

        templateelement.append('<span style="font-weight: bold">Author :{{Author}}</span>' +            '<span  style="font-weight: bold">, {{Purpose}}</span>');
        console.log('compileTrack : Compile Function');

        return function (scope, instanceelement, attrs) {
            scope.Author = "Rajesh";
            scope.Purpose = 'Architect';
            scope.Title = "Angular Sample";
            scope.technology = "Core";

            var funcTemplate = '<br/><div>Technology : {{technology}}</div>';

         
             var compileTemplate = $compile(funcTemplate)(scope);
             instanceelement.append(compileTemplate);


            console.log('compileTrack : Post Link function');
        }
    }
}
});


Output:
********************


Now we see the example with more elaborate with expansion of Link function to PreLink and Post link, to find the order of execution with some sub directives.

Now we modify the above directive with pre and post link functions to demonstrate more details about the  order of executions:

Now we make the compile function with returning a object of two functions prelink and postlink functions, additionally we will create a another directive which is named as degree to find the order the of execution in nested directives.


app.directive('degree', function () {
    return{
        restrict:'E',
        template:'<div>Education  :{{Name}}</div><br/>',
        compile: function (templateElement,attrs) {
            console.log('Degree : Compile Function');

            return{
                pre: function (scope, instanceElement, attrs) {
                    console.log('Degree : pre Function');
                },
                post: function (scope,instanceElement,attrs) {

                    scope.Name = 'Computer Science Engineering';

                    console.log('Degree : Post Function');
                }
            }
        }
    }
});

app.directive('compileTrack',function($compile){
return{
    restrict:'E',
    transclude:true,
    template:'<div>{{Title}}</div><br/><div ng-transclude></div>',
    compile: function (templateelement, attrs) {

   templateelement.append('<span style="font-weight: bold">Author :{{Author}}</span>' +
            '<span  style="font-weight: bold">, {{Purpose}}</span>');
        console.log('compileTrack : Compile Function');

        return {
            pre: function (scope, instanceelement, attrs) {

                scope.Title = "Angular Sample";
                scope.technology = "Core";
                console.log('compileTrack : pre Link function');
            },
            post:function (scope, instanceelement, attrs) {

            scope.Author = "Rajesh";
            scope.Purpose = 'Architect';

            var funcTemplate = '<br/><div>Technology : {{technology}}</div>';

             var compileTemplate = $compile(funcTemplate)(scope);
             instanceelement.append(compileTemplate);


            console.log('compileTrack : Post Link function');
        }

        }
    }
}
});



Output:



Order of Execution:






From this post you can learn what is the real usage of compile function in angular js. and the order of execution in nested directives