7

I'm trying to understand the difference between compile and link function. In the angular documentation for the compiler it says

Some directives such as ng-repeat clone DOM elements once for each item in a collection. Having a compile and link phase improves performance since the cloned template only needs to be compiled once, and then linked once for each clone instance.

I looked at the source code to try and understand how this works and I don't get how it creates a separate linking function for each cloned instance. To me it looks like the compile function returns one linking function for the ng-repeat directive. This linking function does create a new scope for each element in the ng-repeat but doesn't give each cloned instance a separate linking function.

John Slegers
  • 45,213
  • 22
  • 199
  • 169
jvans
  • 2,765
  • 2
  • 22
  • 23

2 Answers2

6

One thing that can be confusing about their description is they're trying to discuss the idea of a directive within an <ng-repeat> as opposed to discussing <ng-repeat> itself.

The idea is that even if you have multiple instantiations of a particular directive (for instance because they are within an <ng-repeat>) the compile function is executed once and only once for the lifetime of your app. Thus, the performance benefit of putting code in here is that it only gets run only once. And that's also the potential problem. The only things that should go in a compile function are things that are common to all the instantiations of that directive.

The link function, on the other hand, is executed once for each instantiation of that directive (again, for example, within an <ng-repeat>).

So you can think of the compile function as setting up the template of what a directive of this type should be, while the link function sets up an actual instance of that directive. This is why the link function gets a $scope passed in to it and the compile doesn't and why the link function is the much more commonly used one.

For a great discussion of exactly this by one of the authors of Angular, check out: http://www.youtube.com/watch?v=WqmeI5fZcho&list=TLwY_LmqLDXW_3QKhTNm1zKa9j40TvPO2O (13:42 is where Misko address link vs compile functions)

KayakDave
  • 24,636
  • 3
  • 65
  • 68
  • Thanks for your answer and the link, they definitely helped a lot. Just to make sure im understanding correctly it seems like you only would use a compile if you're going to use an ng-repeat directive on the element. Otherwise you can do everything and more with the link function so there's no real benefit to using compile. – jvans Oct 19 '13 at 21:16
  • I think you got it. Except it's any situation where you have multiple copies of the directive. ng-repeat is just a convenient way to describe that situation. – KayakDave Oct 19 '13 at 21:22
  • And in both situations the link is still the more commonly used function. As you say, you can do more with the link function since it's attached to a real instantiation with an actual scope. – KayakDave Oct 19 '13 at 21:46
  • It's not really any situation where you have multiple copies of the directive. If you were to put 2 instances of a directive on the same page and log out a message in the compile function you can see that it will be called twice, just like the pre-link and post-link are. Ng-repeat does something specific to ensure that the directive is cloned and its compile function is called only once for all instances of said directive within it. I think this trips a lot of people up so it's worth mentioning. – Gho5t Oct 24 '15 at 03:20
  • So, to be clear, ng-repeat does not use the compile function, is that correct? It's defined, but the only thing it does is return the link function. – John B Feb 15 '17 at 18:04
2

In addtion to KayakDave's answer, here is a simple implementation of ng-repeat that doesn't do any of the collection watching. The plunker has logging that shows the sequence of events and even has some examples that show how priority works.

Plunker

Link to source question

Javascript:

app.directive('fakeRepeat', function() {
  return {
    priority: 1000,
    terminal: true,
    transclude: 'element',
    compile: function(el, attr, linker) {
      return function(scope, $element, $attr) {
        angular.forEach(scope.$eval($attr.fakeRepeat).reverse(), function(x) {
          var child = scope.$new();
          child[attr.binding] = x;
          linker(child, function(clone) {
            $element.after(clone);
          });
        });
      }
    }
  }
});
Community
  • 1
  • 1
mortalapeman
  • 1,415
  • 12
  • 16