Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS: Should I convert directive's linking function to a controller?

I heard it's a good practice to use the controllerAs syntax along with bindToController: true in directives that use an isolate scope. References: one, two

Suppose, I have a directive like this:

angular.module('MyModule').directive('MyDirective', function(User) {
  return {
    scope: {
      name: '='
    },
    templateUrl: 'my-template.html',
    link: function(scope) {
      scope.User = User;
      scope.doSomething = function() {
        // Do something cool
      };
    }
  };
});
<!-- my-template.html -->
<div>
  User Id: {{ User.id }}
  Name: {{ name }}
  <button ng-click="doSomething()">Do it</button>
</div>

As you can see, there is no controller in this directive. But, to be able to leverage controllerAs and bindToController: true I have to have a controller.

Is the best practice to convert the linking function to a controller?

angular.module('MyModule').directive('MyDirective', function(User) {
  return {
    scope: {
      name: '='
    },
    templateUrl: 'my-template.html',
    bindToController: true,
    controllerAs: 'myCtrl',
    controller: function() {
      this.User = User;
      this.doSomething = function() {
        // Do something cool
      };
    }
  };
});
<!-- my-template.html -->
<div>
  User Id: {{ myCtrl.User.id }}
  Name: {{ myCtrl.name }}
  <button ng-click="myCtrl.doSomething()">Do it</button>
</div>

My understanding is that directive's controller should be used as a mechanism to expose directive's API for a directive-to-directive communication.

Could anyone shed light on what's the best practice these days, having Angular 2.0 in mind?

like image 566
Misha Moroshko Avatar asked Jan 05 '15 00:01

Misha Moroshko


2 Answers

I consider it best practice to move initialization code and/or exposing API functions inside of a directive's controller, because it serves two purposes:

1. Intialization of $scope 
2. Exposing an API for communication between directives

Initialization of Scope

Suppose your directive defines a child scope (or inherits scope). If you initialize scope inside of your link function, then child scopes will not be able to access any scope variables defined here through scope inheritance. This is because the parent link function is always executed after the child link function. For this reason, the proper place for scope initialization is inside of the controller function.

Exposing a Controller API

Child directives can access the parent directive's controller through the 'require' property on the directive definition object. This allows directives to communicate. In order for this to work, the parent controller must be fully defined, so that it can be accessed from the child directive's link function. The best place to implement this is in the definition of the controller function itself. Parent controller functions are always called before child controller functions.

Final Thoughts

It is important to understand that the link function and the controller function serves two very different purposes. The controller function was designed for initialization and directive communication, and the linker function was designed for run-time behavior. Based on the intent of your code, you should be able to decide whether it belongs in the controller, or it belongs in the linker.

Should you move any code that initializes scope from the link function to the controller function?

Yes, that is one of the primary reasons that the controller function exists: to initialize scope, and allow its scope to participate in prototypical scope inheritance.

Should you move $watch handlers from the link function to the controller function?

No. The purpose of the link function is to hookup behavior and potentially manipulate the DOM. In the link function, all directives have been compiled, and all child link functions have already executed. This makes it an ideal place to hookup behavior because it is as close DOM ready as it can be (it is not truly DOM ready until after the Render phase).

like image 84
pixelbits Avatar answered Oct 11 '22 03:10

pixelbits


I will start with your last sentence. It's all about how you want to write your angular code. If you want to stick with the guideline for writing good code for angular 1.x then don't even bother thinking too much about what is ideal. However, if you want to prepare for the next version of Angular, as well as, the upcoming web technologies, I would suggest that you start adopting the new concepts and adjust them to the way you write your code today. Bare in mind there is no right or wrong in this case.

Speaking about angular 2.0 and ES6, I would like to stress out that the notion of directives will be more in align with the Web Components technology.

In Angular 2.0 (according to the current design) will get rid of the complex way of defining directives; That is no more DDO. Thus I think it would be better if you start thinking in that way. A component will just have a View and a controller.

For example,

@ComponentDirective({
    selector:'carousel',
    directives:[NgRepeat]
})
export class Carousel{  
    constructor(panes:Query<CarouselItem>) {
        this.items= panes;
    }

    select(selectedCarouselItem:CarouselItem) { ... }
}

The above code is written in AtScript (a superset of typescript and ES6), but you will be able to express the same thing in ES5, as well. You can see how simpler things will be. There in np such notion like link function or compile etc.

In addition, the view of the above component will be directly bound to the above class; So you can already find a similarity to the controllerAs syntax.

So in essence, I would suggest that you first look at the general idea behind Web Components, and how the future of the Web Developments might be, and then I think you would start writing Angular 1.x code with that in mind.

In summary, try to code in a way that favours the current version of Angular, but if you believe that there are some parts of your code that can embrace some concepts of the next version, then do it. I don't believe it will harm you. Try to keep it simple as the new version of Angular will be simpler.

I would suggest that you read the following posts:

  1. https://www.airpair.com/angularjs/posts/component-based-angularjs-directives
  2. http://eisenbergeffect.bluespire.com/all-about-angular-2-0/
  3. https://www.airpair.com/angularjs/posts/preparing-for-the-future-of-angularjs
  4. http://teropa.info/blog/2014/10/24/how-ive-improved-my-angular-apps-by-banning-ng-controller.html
like image 30
ppoliani Avatar answered Oct 11 '22 05:10

ppoliani