AngularJS – Directive compile, pre-link, post-link, controller functions

In this post we will look into the compile and link functions of angular directives. Angular directives are very powerful if used properly. Compile and Link are basic directive functions which need to be understood properly to use angular directives efficiently.

Here is full documentation by angular, on the topics we are going to discuss below https://docs.angularjs.org/api/ng/service/$compile

Order Of Functions

Lets start by defining a simple directive

 var mod = angular.module('Mod', []);
            mod.directive('myDir', function () {
                return {
                    restrict: 'E',
                    controller: function ($scope, $element) {
                        console.log(': controller');
                        console.log($element.html());
                    },
                    compile: function (tElem, tAttrs) {
                        console.log(': compile');
                        console.log(tElem.html());
                        return {
                            pre: function (scope, iElem, iAttrs) {
                                console.log(': pre link');
                                console.log(iElem.html());
                            },
                            post: function (scope, iElem, iAttrs) {
                                console.log(': post link');
                                console.log(iElem.html());
                            }
                        }
                    }
                }
            });

Let’s use it in our html code like this

<my-dir>
     Parent
     <my-dir>Child</my-dir>
</my-dir>

Above we have defined a very simple directive using all important functions of a directive. We have nested that directly in itself and added parent/child text in the template. Now if we see the output we know the order in which these directives get executed.

: compile
Parent
<my-dir>Child</my-dir>
: compile
Child
: controller
Parent
<my-dir>Child</my-dir>
: pre link
Parent
<my-dir>Child</my-dir>
: controller
Child
: pre link
Child
: post link
Child
: post link
Parent
<my-dir>Child</my-dir>

Looking at the above we can see the order in which these functions execute. This order is always very important, the reason we will see later.

So to conclude the order is:
Compile Parent -> Compile Child -> Controller Parent -> PreLink Parent -> Controller Child -> PreLink Child -> Post Link Child -> Post Link Parent

PreLink/PostLink Function

In above example, we saw that prelink of parent gets execute first but the post link of child gets executed first.
Lets see why this is important with an example

            var app = angular.module('app', []);
            app.directive('myDad', function () {
                return {
                    restrict: 'E',
                    link: function (scope, elem, attr) {
                        scope.name = 'XXX';
                        scope.greeting = 'Hello!';
                    }
                };
            });
            app.directive('mySon', function () {
                return {
                    restrict: 'E',
                    link: function (scope, elem, attr) {
                        scope.sonSays = 'Hey, I am son, and my dad is ' + scope.name;
                    }
                };
            });
     <my-dad>
        {{greeting}}
        <br/>
        <my-son>
            {{sonSays}}
        </my-son>
    </my-dad>

In the above code we have created two directive “son” and “dad”. The output of above code is

Hello!
Hey, I am son, and my dad is undefined 

As you can see, the scope.name comes to be undefined.
The reason for this is, postLink of child is called before parent, hence the scope variable is not present. To fix this, we need to use the preLink function instead.

            app.directive('myDad', function () {
                return {
                    restrict: 'E',
                    compile: function (elem, attr) {
                        return {
                            pre: function (scope) {
                                scope.name = 'XXX';
                                scope.greeting = 'Hello!';
                            }
                        }
                    }
                };
            });

The above code fixes the problem and we get correct output.
But prelink function is rarely used for this purpose. Lets see what the correct way to share scope through controller.

Sharing Scope Through Controller

In the above example we saw how to pass scope to child directive from parent to child, we did this using preLink function above. There is a better way to do this via controller option which the directive api provide, the preLink function is used very rarely.

            var app = angular.module('app', []);
            app.directive('myDad', function () {
                return {
                    restrict: 'E',
                    controller: function ($scope) {
                        $scope.name = 'XXX';
                        $scope.greeting = 'Hello!';
                    }
                };
            });
            app.directive('mySon', function () {
                return {
                    restrict: 'E',
                    require: '^myDad',
                    link: function (scope, elem, attr) {
                        scope.sonSays = 'Hey, I am son, and my dad is ' + scope.name;
                    }
                };
            });

In the above, we are using the controller function of a directive and setting scope through this. Also child directive, “require” the parent directive. This way we are establishing a dependency as well and child directive wont work without parent. In the controller we can inject services as usual, do event handing and all other operations.

Directive Compile Function

The directive compile function is the place where you can make any DOM changes which are required, during this phase the directive is still a raw html, no events etc have been attached.
So in summary this is place to make changes in html if any.

You can also add HTML is preLink, postLink function as well but since DOM events, directive are already attached they wont get processed.

In the above code, i have added html in both compile and postLink function. As you can see ng-click works on the compile and not the postLink.

Above we have discussed in detail about the compile, preLink, postLink and controller function of angularjs.