Angular Directive Lifecycle: Compile, Pre-link, Post-link, and Controller

Angular directives have a lifecycle that involves several phases: compilation, linking (pre-link and post-link), and controller execution. Understanding these phases is crucial for efficiently managing directive behavior and DOM manipulation.

Directive Definition Object and Linking Functions

The directive definition object in Angular can have both compile and link properties. Their interaction is as follows:

  • If a compile function is present, the link function is ignored. The return value of the compile function is used as the linking function.
  • If compile is absent but link is present, Angular wraps the user-defined link function using directive.compile = valueFn(directive.link);.
  • If both compile and link are present, the link property is ignored, and the compile function's return value dictates the linking behavior.

The link function itself can be structured in two ways:

  • If it's a single function, it's treated as a postLink function.
  • If it's an object with pre and post properties, these are treated as the preLink and postLink functions, respective.

app.directive('myDirective', function() {
 return {
   compile: function() {
     console.log('Compile phase');
     // Returning an object to define pre and post link functions
     return {
       pre: function(scope, element, attrs) {
         console.log('Pre-link phase');
       },
       post: function(scope, element, attrs) {
         console.log('Post-link phase');
       }
     };
   }
 };
});
 

Execution Order

Angular processes directives starting from the root element annotated with ng-app, recursively traversing down the DOM. Each directive undergoes compilation and linking.

  • Compilation: The compile function is executed once per directive definition, regardless of how many instances of the directive exist.
  • Linking: The link function (including preLink and postLink) is executed for each directive instance.

Compile Phase Order

Compile functions are executed in a depth-first, left-to-right manner: from parent to child elements, and then across sibling elements, respecting directive priorities.

Pre-link and Post-link Phase Order

After all compile functions have run, the linking phase begins.

  1. Pre-link: Executed in a depth-first, top-to-bottom order (parent to child). For a given element, its preLink runs before its children's preLink and postLink.
  2. Post-link: Executed in a bottom-up, right-to-left order (child to parent). For a given element, its postLink runs after all its children's preLink and postLink have completed.

The overall linking process follows this flow:

  1. Execute the current element's directive's preLink.
  2. If the element has child directives, recursively execute their preLink functions.
  3. Once all child preLink functions are done, execute the current element's directive's postLink.
  4. If the element has sibling directives, move to the next sibling and repeat the process from its preLink.
  5. After an element's postLink completes and it has no more siblings to process, its parent's postLink can execute.

Purpose of Each Phase

Compile Function

The compile function receives the template element and its attributes. It's executed before any scope or element instances are created. This phase is ideal for:

  • Modifying the template DOM structure.
  • Performing operations that should occur only once per directive definition, like cloning template structures for use in directives like ng-repeat. This can improve performance by avoiding repeated DOM manipulation for multiple instances.

Note: The scope is not available during the compile phase.

Pre-link Function

The preLink function runs after compile and before the directive's child elements are linked. It receives the scope and the element instance.

  • Ideal for passing data or setting up scope properties that child directives might need during their own linking phase.

Post-link Function

The postLink function executes after the directive's children have completed their linking phases. It receives the scope and the element instance.

  • This is the most common place for DOM manipulation, event binding, and interacting with child elements becuase the entire DOM subtree for the directive is stabilized.
  • Considered the safest place for most business logic related to the directive's instance.

Controller

The controller is instantiated after the compile phase and *before* the preLink phase.

  • Controllers can define data or methods that are accessible to the directive's linking functions (preLink and postLink).
  • They can also expose an API that other directives on the same element can interact with. Directives can require another directive's controller using the require property in their definition.

A controller can be required by another directive by referencing its name, often in the format 'directiveNameController'.


// Example directive with a controller and linking functions
app.directive('parentDirective', function() {
 return {
   restrict: 'E',
   controller: function($scope, $element, $attrs) {
     this.sharedData = 'Data from parent controller';
     console.log('Parent: Controller');
   },
   compile: function(tElement, tAttrs) {
     console.log('Parent: Compile');
     return {
       pre: function(scope, iElement, iAttrs) {
         console.log('Parent: Pre-link');
         // Can potentially access controller if required, but not ideal here
       },
       post: function(scope, iElement, iAttrs) {
         console.log('Parent: Post-link');
         // Access controller via iElement.controller('parentDirective') or scope.$parent.parentDirective if exposed
       }
     };
   }
 };
});

app.directive('childDirective', function() {
 return {
   restrict: 'E',
   require: '^^parentDirective', // Require parent directive's controller
   link: function(scope, iElement, iAttrs, parentCtrl) {
     console.log('Child: Link');
     console.log('Child received from parent:', parentCtrl.sharedData);
   },
   compile: function(tElement, tAttrs) {
     console.log('Child: Compile');
     return {
       pre: function(scope, iElement, iAttrs) {
           console.log('Child: Pre-link');
       },
       post: function(scope, iElement, iAttrs) {
           console.log('Child: Post-link');
       }
     }
   }
 };
});
 

Tags: AngularJS Directives Lifecycle compile link

Posted on Fri, 07 Aug 2026 16:43:00 +0000 by tasistro