AngularJs : Custom directive compile,link and transclude

In the previous post we saw custom directive and about isolated scope. In this post we will look into the compile and link functions of angular directives. Compile and Link are basic directive functions which need to be understood properly to use angular directives efficiently. Lets see this by our previous example.
We had a custom directive book-result in our index.html


<!DOCTYPE html>
<html lang="en-us" ng-app="angularApp">
<head>
<title>Html Link Fragments</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" />
</head>
<body>

<header>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand">AngularJS Demo</a>
</div>
</div>
</nav>
</header>
<div class="container" ng-controller="mainController">
<div class="list-group">
<div ng-repeat="book in books">
<book-result book-object="book"></book-result>
</div>
</div>
</div>
</body>
<script src="https://code.angularjs.org/1.4.8/angular.min.js"></script>
<script src="../js/main.js"></script>
</html>

In our main.js we have a array of books onto our $scope and the definition of our directive.


var myApp = angular.module('angularApp', []);
myApp.controller('mainController', ['$scope', function($scope) {

$scope.books=[
{
title:'Book1',
description:'Book1 description'
},
{
title:'Book2',
description:'Book2 description'
}
]

}]);

myApp.directive("bookResult", function(){

return{
templateUrl:'directives/bookResultDirective.html',
replace:true,
scope:{
bookObject: '='
},
compile: function (tElem, tAttrs) {
console.log(': compile');
console.log(tElem);
return {
pre: function (scope, iElem, iAttrs) {
console.log(': pre link');
console.log(iElem);
},
post: function (scope, iElem, iAttrs) {
console.log(': post link');
console.log(iElem);
}
}
}
}

});

Notice the compile property above. Its a function that takes 2 arguments, element and attributes. This return an object of 2 functions pre and post. Both these are functions that take in scope,elements,attributes as argument. bookResultDirective.html


<a href="#" class="list-group-item">
<h4>{{bookObject.title}}</h4>
<p>{{bookObject.description}}</p>
</a>

Lets run and see the result

customDirectiveCompileLink1

In compile we have access to the html that defines the view of the directives. It runs only once. In the compile phase we can change the DOM before it gets used by the link functions. After compile, the pre and post runs each time there are items in your object.
Now it may be that we have multiple custom directives nested within other custom directives. In that case angularjs first compiles the directive and run pre-link that looks for any other inside. If it finds one it runs compile again and the runs pre-link and so forth. At the end of the chain it run post-link and comes back-up the chain.

Post-link is safer than pre-link so its recommended to use post-link only.

Also note that the function arguments names can be anything. Its not dependency injections like $scope or other services. You can call these arguments by any name , just that the order must be same.

It would be rare that you use compile and typically you will be using post-link function only if you want to change or add any listener or handler. AngularJS provided a shorthand for that.


myApp.directive("bookResult", function(){

return{
templateUrl:'directives/bookResultDirective.html',
replace:true,
scope:{
bookObject: '='
},
link:function (scope, iElem, iAttrs) {
console.log(': post link');
console.log(iElem);
}

}

});

This would be same as having an empty compile function with post link function. Result:

customDirectiveCompileLink2

Transclusion 

Suppose you have some text in side your custom book-result directive in index.html


<div class="list-group">
<div ng-repeat="book in books">
<book-result book-object="book">*search result powered by Angular</book-result>
</div>
</div>

If you run this angular will replace this custom tag with the actual directive view. To utilize the original DIV content (now replaced by the template), the directive must clone it and add it to the DOM using transclusion. To say it another way, transclusion is including one document into another.

Angular provided us with a property transclude to achieve the same.


myApp.directive("bookResult", function(){

return{
templateUrl:'directives/bookResultDirective.html',
replace:true,
scope:{
bookObject: '='
},
transclude:true
}

});

Changing in bookResultDirective.html


<a href="#" class="list-group-item">
<h4>{{bookObject.title}}</h4>
<p>{{bookObject.description}}</p>
<ng-transclude></ng-transclude>
</a>

customDirectiveTransclusion

The second method to do transclusion is to use the transclude function provided in the post-link function of a directive. You can append to the element in the function.

%d bloggers like this: