536

When you create a directive, you can put code into the compiler, the link function or the controller.

In the docs, they explain that:

  • compile and link function are used in different phases of the angular cycle
  • controllers are shared between directives

However, for me it is not clear, which kind of code should go where.

E.g.: Can I create functions in compile and have them attached to the scope in link or only attach functions to the scope in the controller?

How are controllers shared between directives, if each directive can have its own controller? Are the controllers really shared or is it just the scope properties?

BSMP
  • 4,596
  • 8
  • 33
  • 44
schacki
  • 9,401
  • 5
  • 29
  • 32
  • 12
    See also http://stackoverflow.com/questions/12546945/difference-between-the-controller-and-link-functions-when-defining-an-angula – Mark Rajcok Mar 28 '13 at 16:36
  • Perhaps a more comprehensive overview of directive functions: [Angular directives - when to use compile, controller, pre-link and post-link](http://stackoverflow.com/questions/24615103). – Izhaki Jul 07 '14 at 16:30
  • 1
    I wrote a post with a diagram of directive's lifecycle (creation phase). Maybe it helps someone: http://filimanjaro.com/2014/angular-directive-lifecycle-vs-link-compile-controller/ – average Joe Oct 22 '14 at 07:38
  • Possible duplicate of [Difference between the 'controller', 'link' and 'compile' functions when defining a directive](https://stackoverflow.com/questions/12546945/difference-between-the-controller-link-and-compile-functions-when-definin) – David Mulder Jun 08 '17 at 10:35

6 Answers6

474

Compile :

This is the phase where Angular actually compiles your directive. This compile function is called just once for each references to the given directive. For example, say you are using the ng-repeat directive. ng-repeat will have to look up the element it is attached to, extract the html fragment that it is attached to and create a template function.

If you have used HandleBars, underscore templates or equivalent, its like compiling their templates to extract out a template function. To this template function you pass data and the return value of that function is the html with the data in the right places.

The compilation phase is that step in Angular which returns the template function. This template function in angular is called the linking function.

Linking phase :

The linking phase is where you attach the data ( $scope ) to the linking function and it should return you the linked html. Since the directive also specifies where this html goes or what it changes, it is already good to go. This is the function where you want to make changes to the linked html, i.e the html that already has the data attached to it. In angular if you write code in the linking function its generally the post-link function (by default). It is kind of a callback that gets called after the linking function has linked the data with the template.

Controller :

The controller is a place where you put in some directive specific logic. This logic can go into the linking function as well, but then you would have to put that logic on the scope to make it "shareable". The problem with that is that you would then be corrupting the scope with your directives stuff which is not really something that is expected. So what is the alternative if two Directives want to talk to each other / co-operate with each other? Ofcourse you could put all that logic into a service and then make both these directives depend on that service but that just brings in one more dependency. The alternative is to provide a Controller for this scope ( usually isolate scope ? ) and then this controller is injected into another directive when that directive "requires" the other one. See tabs and panes on the first page of angularjs.org for an example.

ganaraj
  • 26,841
  • 6
  • 63
  • 59
  • 67
    To clarify: compile compiles the template to be used throughout the page. Linker is tied to each instance. Right? Controller then works between instances. – Zlatko Jan 22 '14 at 18:07
  • So Controllers talk to other Controllers using a Service but Directives talk to other Directives using another Directive that each directive requires? Seems like it would be easier just to use Services for shared data, you're still requiring a dependency for each but this way you don't have 2 types of Directives. One that's used as a Meta-Directive/Service thingy and one that's a regular Directive... – user2483724 Mar 11 '14 at 22:39
  • Revising above comment. I found out it's useful b/c the reason a directive would want to talk to another directive is to use some of its functions. This method then allows directives to extend the functionality of another directive. – user2483724 Mar 13 '14 at 18:24
  • really good answer, would be really nice with a image, that shows a mind map over how things should run on angular, sometimes its really confusing since some people talks about services and then they define it as a factory, my guess was from the start that thy would run in diffrent orderes... – Martea Aug 27 '14 at 15:03
  • 5
    @CMCDragonkai for each directive `controller` function is executed **after** compilation, but **before** `pre-link` in a local DOM tree branch. Also `controller` and `pre-link` functions are executed traversing the local DOM branch in a **top-down** manner. After that `post-link` is executed in a **bottom-up** manner. – Artem Platonov Sep 30 '14 at 09:24
  • So.. since you can inject $element into the controller of a directive, why not just always use controller? What is the actual benefit of using the link function? – RavenHursT Oct 07 '14 at 19:50
  • 9
    It's only a mess if you don't understand it. There is a reason for it to do what it does. – demisx Feb 24 '15 at 07:41
  • 3
    This is the correct technical answer, however, I am still left with questions on when I should use the link function. – Nicholas Marshall May 12 '15 at 16:33
  • 1
    If your directive has an isolate scope I'm not sure why attaching things to it on the post-link function is any more polluting than doing it in the controller. – Casey Sep 08 '15 at 19:12
  • 1
    @demisx understanding a mess does not magically make it not a mess. the majority of proprietary code is a mess, and the people who made it understand it. the reason for the way ng 1.x does things the way it does is accumulation of rot, and is one of the reasons the ng team is abandoning it. – jajdoo Dec 07 '15 at 07:29
  • 2
    Shall we use `controller` instead of `link` everywhere? So that I don't need to change the code in future if the method needs to be shared or some logic to be introduced?. Is there any pitfalls in using `controller` all the time instead of link? – JPS Dec 14 '15 at 14:21
  • All this complexity was, I suppose, neded when developing angular own directives, maybe in 3rd parties modules, but for developpers using the library this is just too complicated, same thing with transclude. People do not have much time to learn a new framework, most people will just try to find a woraround to those complex things. – Paolo Aug 13 '17 at 09:25
100

I wanted to add also what the O'Reily AngularJS book by the Google Team has to say:

Controller - Create a controller which publishes an API for communicating across directives. A good example is Directive to Directive Communication

Link - Programmatically modify resulting DOM element instances, add event listeners, and set up data binding.

Compile - Programmatically modify the DOM template for features across copies of a directive, as when used in ng-repeat. Your compile function can also return link functions to modify the resulting element instances.

Community
  • 1
  • 1
  • Your thinkster.io link cannot be watched without paying. Not my link, but perhaps this is more suitable : https://toddmotto.com/directive-to-directive-communication-with-require/ – R. van Twisk Jan 15 '17 at 15:14
53

A directive allows you to extend the HTML vocabulary in a declarative fashion for building web components. The ng-app attribute is a directive, so is ng-controller and all of the ng- prefixed attributes. Directives can be attributes, tags or even class names, comments.

How directives are born (compilation and instantiation)

Compile: We’ll use the compile function to both manipulate the DOM before it’s rendered and return a link function (that will handle the linking for us). This also is the place to put any methods that need to be shared around with all of the instances of this directive.

link: We’ll use the link function to register all listeners on a specific DOM element (that’s cloned from the template) and set up our bindings to the page.

If set in the compile() function they would only have been set once (which is often what you want). If set in the link() function they would be set every time the HTML element is bound to data in the object.

<div ng-repeat="i in [0,1,2]">
    <simple>
        <div>Inner content</div>
    </simple>
</div>

app.directive("simple", function(){
   return {
     restrict: "EA",
     transclude:true,
     template:"<div>{{label}}<div ng-transclude></div></div>",        
     compile: function(element, attributes){  
     return {
             pre: function(scope, element, attributes, controller, transcludeFn){

             },
             post: function(scope, element, attributes, controller, transcludeFn){

             }
         }
     },
     controller: function($scope){

     }
   };
});

Compile function returns the pre and post link function. In the pre link function we have the instance template and also the scope from the controller, but yet the template is not bound to scope and still don't have transcluded content.

Post link function is where post link is the last function to execute. Now the transclusion is complete, the template is linked to a scope, and the view will update with data bound values after the next digest cycle. The link option is just a shortcut to setting up a post-link function.

controller: The directive controller can be passed to another directive linking/compiling phase. It can be injected into other directices as a mean to use in inter-directive communication.

You have to specify the name of the directive to be required – It should be bound to same element or its parent. The name can be prefixed with:

? – Will not raise any error if a mentioned directive does not exist.
^ – Will look for the directive on parent elements, if not available on the same element.

Use square bracket [‘directive1′, ‘directive2′, ‘directive3′] to require multiple directives controller.

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

app.controller('MainCtrl', function($scope, $element) {
});

app.directive('parentDirective', function() {
  return {
    restrict: 'E',
    template: '<child-directive></child-directive>',
    controller: function($scope, $element){
      this.variable = "Hi Vinothbabu"
    }
  }
});

app.directive('childDirective', function() {
  return {
    restrict:  'E',
    template: '<h1>I am child</h1>',
    replace: true,
    require: '^parentDirective',
    link: function($scope, $element, attr, parentDirectCtrl){
      //you now have access to parentDirectCtrl.variable
    }
  }
});
Thalaivar
  • 23,282
  • 5
  • 60
  • 71
  • 1
    you mentioned you showed how to get the parentDirectiveCtrl into the the child's controller... this example the child doesn't have a controller, but rather link function... I'm not stuck on this issue currently, so it might not be so important, but a curious question. – alockwood05 Nov 05 '15 at 01:05
13

Also, a good reason to use a controller vs. link function (since they both have access to the scope, element, and attrs) is because you can pass in any available service or dependency into a controller (and in any order), whereas you cannot do that with the link function. Notice the different signatures:

controller: function($scope, $exceptionHandler, $attr, $element, $parse, $myOtherService, someCrazyDependency) {...

vs.

link: function(scope, element, attrs) {... //no services allowed
ScaryBunny
  • 221
  • 2
  • 3
  • 2
    Please leave a comment to explain your point when you downvote an answer. Thanks – svassr Nov 22 '13 at 18:26
  • 56
    I wasn't the downvoter, but this is not strictly correct because you can still inject any required dependency into the directive itself, eg: `module.directive('myDirective', function($window) { etc...`. This can then be accessed from inside the link function. – Mike Chamberlain Dec 02 '13 at 04:35
  • 1
    A better explanation of the difference in what you can inject into the link, compile, or controller functions is http://stackoverflow.com/a/14300374/275581. The link function doesn't use dependency injection, and is passed exactly the arguments above. The compile function uses dependency injection bound to an individual instance of the directive, so it has all the global dependencies available plus local ones like `$scope`. Ditto for the controller. The directive itself is instantiated more globally and doesn't get the per-instance dependencies (no `$scope`). – metamatt Jun 19 '14 at 17:08
  • 2
    this seems to be straightforwardly incorrect as you can inject services into the link function – Code Whisperer Aug 15 '14 at 13:01
  • Would be interesting to see a plunker of a service being injected into a link function. – demisx Feb 24 '15 at 07:45
  • That example is injecting into the directive function, which is accessible to the link() function through the closure scope. That plnkr is not an example of injecting services into the link function per se, which is impossible. – Josh Ribakoff May 12 '15 at 23:31
  • 1
    @JoshRibakoff The end result is the same, you have access to the service in the link function. It doesn't matter whether it is declared in the arguments of the function or not. In this regard Mike Chamberlain is correct – Connor Wyatt Jul 30 '15 at 18:00
  • 1
    @cwyatt1 I was correcting parlance, the plnkr does not show injecting into a link() function because that is not a feature Angular has. You might think I'm being pedantic but metamatts comment already outlines numerous important differences between what that plunkr does, and what injecting to a controller does. The OP is asking what the differences are, and there are differences. – Josh Ribakoff Jul 31 '15 at 20:08
  • @scareBunny This is not ture, you can use service as well. – Scipion Dec 14 '15 at 08:51
10

this is a good sample for understand directive phases http://codepen.io/anon/pen/oXMdBQ?editors=101

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

app.directive('slngStylePrelink', function() {
    return {
        scope: {
            drctvName: '@'
        },
        controller: function($scope) {
            console.log('controller for ', $scope.drctvName);
        },
        compile: function(element, attr) {
            console.log("compile for ", attr.name)
            return {
                post: function($scope, element, attr) {
                    console.log('post link for ', attr.name)
                },
                pre: function($scope, element, attr) {
                    $scope.element = element;
                    console.log('pre link for ', attr.name)
                        // from angular.js 1.4.1
                    function ngStyleWatchAction(newStyles, oldStyles) {
                        if (oldStyles && (newStyles !== oldStyles)) {
                            forEach(oldStyles, function(val, style) {
                                element.css(style, '');
                            });
                        }
                        if (newStyles) element.css(newStyles);
                    }

                    $scope.$watch(attr.slngStylePrelink, ngStyleWatchAction, true);

                    // Run immediately, because the watcher's first run is async
                    ngStyleWatchAction($scope.$eval(attr.slngStylePrelink));
                }
            };
        }
    };
});

html

<body ng-app="myapp">
    <div slng-style-prelink="{height:'500px'}" drctv-name='parent' style="border:1px solid" name="parent">
        <div slng-style-prelink="{height:'50%'}" drctv-name='child' style="border:1px solid red" name='child'>
        </div>
    </div>
</body>
Amin Rahimi
  • 235
  • 3
  • 7
  • 5
    Could you elaborate on why this sample code would help to understand the difference between `link`, `compile` and `controller`? – cel sharp Aug 17 '15 at 07:56
  • do you know how a `require`d directive can be injected into a dependent directive's controller? – alockwood05 Sep 24 '15 at 22:18
  • You codepen example: Uncaught Error: [$injector:modulerr] Failed to instantiate module myapp due to: Error: [$injector:unpr] Unknown provider: slngStylePrelinkProvider – rofrol Nov 08 '16 at 11:35
8
  • compile: used when we need to modify directive template, like add new expression, append another directive inside this directive
  • controller: used when we need to share/reuse $scope data
  • link: it is a function which used when we need to attach event handler or to manipulate DOM.
perror
  • 7,071
  • 16
  • 58
  • 85
HamidKhan
  • 545
  • 1
  • 6
  • 9