1

I have the following code. I expected the compile to run only once, and link to run 5 times.

<html>
<head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js" ></script> 
    <style>.red{color:red;}.blue{background:blue;}</style>
</head>
<body ng-app="myApp" ng-controller="myCtrl"> 
<div hello dear> 1 </div>
<div hello dear> 2 </div>
<div hello dear> 3 </div>
<div hello dear> 4 </div>
<div hello dear> 5 </div>
<script>
//module declaration
var app = angular.module("myApp",[]);
//controller declaration
app.controller('myCtrl',function($scope){
    //no code
});
//directives declaration
app.directive('hello',function(){
    return{
        restrict: 'A',
        compile: function(tElement, tAttributes){
            tElement.addClass("red");
            console.log("compiled");
        },
    }
});
app.directive('dear',function(){
    return{
        restrict: 'A',
        link: function($scope, element, attributes){
            element.addClass("blue");
            console.log("linked");
        }
    }
});
</script> 
</body> 
</html>

Expectation:

Compile to run once. Link to run 5 times.

Result:

Both compile and Link running 5 times.

Screen-shot:

enter image description here

Reference:

http://www.bennadel.com/blog/2794-when-do-you-need-to-compile-a-directive-in-angularjs.htm

Can someone tell me why compile running 5 times (OR, how to run it only once)?

Deadpool
  • 7,811
  • 9
  • 44
  • 88

2 Answers2

1

The compile phase comes before the link phase for every directive, since you applied the hello directive on 5 elements it's expected behaviour to compile and then link 5 times. A more detailed explanation of the linking process of a directive with a subdirective can be found here.

Compile function

Each directive's compile function is only called once, when Angular bootstraps.

Officially, this is the place to perform (source) template manipulations that do not involve scope or data binding.

Primarily, this is done for optimisation purposes; consider the following markup:

<tr ng-repeat="raw in raws">
    <my-raw></my-raw>
</tr>

The <my-raw> directive will render a particular set of DOM markup. So we can either:

Allow ng-repeat to duplicate the source template (<my-raw>), and then modify the markup of each instance template (outside the compile function). Modify the source template to involve the desired markup (in the compile function), and then allow ng-repeat to duplicate it. If there are 1000 items in the raws collection, the latter option may be faster than the former one.

Do:

  • Manipulate markup so it serves as a template to instances (clones).

Do not:

  • Attach event handlers.
  • Inspect child elements.
  • Set up observations on attributes.
  • Set up watches on the scope.
Community
  • 1
  • 1
Martijn Welker
  • 5,575
  • 1
  • 16
  • 26
1

That's the intended behavior though. Have a look at https://docs.angularjs.org/api/ng/service/$compile

The whole flow (compile, controller, pre/post-link) happens for each directive tag.

This would make your compile run only once:

<hello>
    <div dear> 1 </div>
    <div dear> 2 </div>
    <div dear> 3 </div>
    <div dear> 4 </div>
    <div dear> 5 </div>
</hello>

Though in that case, there is no need to use the compile phase, just use the link

app.directive('hello',function(){
  return{
    restrict: 'AE',
    link: function(scope, iElement){
        iElement.children().addClass("red");
        console.log("\"compiled\"");
    },
  }
});

Edit: if you need hello directive's code to run before dear's, put the code in prelink instead of link.

.directive('hello', function() {
  return {
    restrict: 'AE',
    compile: function() {
      return {
        pre: function(scope, iElement) {
          iElement.children().addClass("red");
          console.log("\"compiled\"");
        }
      }
    },
  }
})
FloG
  • 463
  • 5
  • 9