Difference Between the 'Controller', 'Link' and 'Compile' Functions When Defining a Directive

using link, compile and controller function together in directive

Link function is a part of compile function. If you defined compile, you override the compile function, pre link function and post link function.
Your can write your compile function like this :

compile: function(elem,attrss){
console.log("compile function called");
return {
pre: function() { console.log("pre link function called"); },
post: function() { console.log("post link function called, it's the same of link function"); }
}
}

So it useless to defined link in directive if you override compile function. And link doesn't will be called.
I create a little plunker to illustrate it https://plnkr.co/edit/hbel2uGzbyp0VHfQS4pN?p=preview.

Angular call function in this order :

  • Create the scope for the directive (depends on config)
  • Parse DOM from top to bottom (foreach node in DOM)

    • call compile function
    • call controller function
    • call pre link function
  • Parse DOM from bottom to top

    • call post link function

I recommend to you to read this post Angular directives - when and how to use compile, controller, pre-link and post-link

What is the difference between controller and directives in angular js?

There's a bit too much going on here to be able to fully explain each so i'll try to give a brief explanation of each as well as an example.

Controllers

Use controllers to handle the logic of your views and assign the data you want to appear in your view. For example if in your application you have a page called "All Users" you want to display a list of users you would, define an array of users and attach it to the $scope object in your controller.

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

myApp.controller('allUsersController', ['$scope', function($scope) {
$scope.users = [
{ name: "User 1", id: 1},
{ name: "User 2", id: 2},
{ name: "User 3", id: 3}
];
}]);

Attaching the users array to the scope allows you to access this data from the view. So now in the view you can use ng-repeat to output the list of users:

<ul>
<li ng-repeat="user in users">{{user.name}}</li>
<ul>

Directives

Directives are mainly used for 2 things:

  1. Creating reusable components
  2. Manipulating the DOM

Directives are tricky to use at first but are extremely powerful, this statement from the docs is the reason why they are so powerful:

Directives are markers on a DOM element (such as an
attribute, element name, comment or CSS class) that tell AngularJS's
HTML compiler ($compile) to attach a specified behavior to that DOM
element (e.g. via event listeners), or even to transform the DOM
element and its children.

The main point to take away from that is that directives allow you to attach certain logic/behavior to a certain element, where as controllers usually only allow you to attach logic to pages/views.

Lets say that in the previous example we wanted to add some actions that can be done in the user list, maybe a like and dislike button. We could just create like and dislike buttons like this:

JS

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

myApp.controller('allUsersController', ['$scope', function($scope) {
$scope.users = [
{ name: "User 1", id: 1, like: 0},
{ name: "User 2", id: 2, like: 0},
{ name: "User 3", id: 3, like: 0}
];

$scope.like = function(user){
user.like++;
}

$scope.dislike = function(user){
user.like--;
}
}]);

HTML

<ul>
<li ng-repeat="user in users">
{{user.name}}
<button ng-click="like(user)">LIKE</button>
<button ng-click="dislike(user)">DISLIKE</button>
</li>
<ul>

Fairly simple, we add like/dislike methods in our controller that increment/decrement the amount of likes a user has. This code will work fine, but what if i wanted to create another list of users in a different view? Say that you have 3 different views which contain user lists: "All Users", "My Friends" and "Recommended Users", all 3 will have a list of users that have the same actions (like or dislike) but the users displayed are different in each. We want to use the same like/dislike methods that we defined in our allUsersController but we are on a different view so we can't access them, so you would have to copy the same code into the controllers of the other views, might not seem like a big deal in our example but as applications get larger and more complicated this gets very tedious and hard to maintain.

This is where directives come in, instead of assigning the logic behind each user item in the controller you can define it in a directive:

app.directive('userItem', function() {
return {
template: '<div>{{userData.name}} <button ng-click="like()">Like</button> <button ng-click="dislike()">Dislike</button>',
scope: {
userData: "="
},
link: function(scope, element, attrs) {
scope.like = function(){
scope.userData.like++;
}

scope.dislike = function(){
scope.userData.like--;
}
}
}
});

In your html:

<div class="user_list>
<user-item ng-repeat="user in users" user-data="user"></user-item>
</div>

By using the user-item directive you can now create a list of users anywhere in your application without having to redefine the logic that goes with each user. You'll notice that this also cleans up our html a bit and saves you on repeating code a lot. The directive wraps up your html and js into a reusable component.

EDIT:
Regarding how we're passing the user data to the directive, this has to do with the isolate scope in directives which you can read about here. The basic idea is that it isolates the scope of the directive from the parent scope (allUsersController in our case), this is done to avoid unwanted clashes between the data in the 2 scopes and to promote re-usablity. But at the same time there is some data that we want the controller to share with the directive, so we "poke a hole" through the isolate scope to allow certain things in, which in our case is the userData defined in the directive scope.

You can visit the directives docs and scroll down to isolate scopes for more examples.

Angular directive: whats the difference between scope in controller vs scope in link function?

Scope is specific to current directive instance and is the same object in both functions.

For defining methods on the scope, there's no difference if they are defined in controller or link function, unless there is race condition that requires the method to be defined as early as possible. For this reason it makes sense to define scope methods in controller.

Event handler doesn't differ from any other function, it is

elem.on("click", function (e) {
scope.$apply(function () {
scope.character...
});
});

scope.$apply(...) wrapper doesn't hurt anyway, but the necessity of it depends on what happens with scope.character.

The directive can have only controller and no link. Current Angular versions (1.5+) suggest the style where bindToController + controllerAs are used instead of scope bindings as common ground for directives and components.

Then the directive may look like

restrict: "E",
template: '<p>{{$ctrl.character.name}}</p>',
controllerAs: '$ctrl',
bindToController: { character: "=" },
controller: function ($element, $scope) {
var self = this;

self.getData = function (data) { ... };

$element.on("click", function (e) {
scope.$apply(function () {
self.character...
});
});
}

link function may appear as $postLink controller hook, but here it is not needed.

AngularJS Directives: Are Link and Compile functions meant to work together?

link and compile do not work together, no.

In the directive definition object, if you only define link, that's like shorthand for having an empty compile function with an empty preLink function with your code in the postLink function. As soon as you define compile, link is ignored by angular, because compile should return the linking functions.

If you only return one function from compile, then it'll be executed post link.

Or, put differently, link is just a shortcut to the postLink function that gets called after the scope has been linked by compile.

It's (sort of) documented here - look at the comments in the code sample.

Difference between $watch expressions in directive link vs controller function?

There is no difference as far as I can tell. The scope passed in the linking is: "Scope - The scope to be used by the directive for registering watches."

So if you don't explicitly declare a controller or scope for the directive you still have a scope (taken from the context in which the directive is being compiled) to add watches to in the link function returned by the compile.

Angular Directive - Compile Vs Link - Compile not rendering only once

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.


Related Topics



Leave a reply



Submit