Angular Icon Get 73% off the Angular Master bundle

See the bundle then add to cart and your discount is applied.

0 days
00 hours
00 mins
00 secs

Write AngularJS like a pro. Angularjs Icon

Follow the ultimate AngularJS roadmap.

Dynamic Controllers in Directives with the undocumented name property

Assigning Controllers to Angular Directives is the norm when building out components. But what if you wanted to dynamically assign a Controller to the same Directive and template? There are reasons for using this technique (though uncommon), and the undocumented name property bound to each Directive’s definition Object can allow us to do exactly that.

Hard-coded Controller lookups

Let’s look at a “hard-coded” Directive Controller which we use everyday:

function FirstCtrl() {
  this.name = 'First Controller';
}
function fooDirective() {
  return {
    scope: {},
    controller: 'FirstCtrl',
    controllerAs: 'foo',
    template: '<div>{{ foo.name }}</div>',
    link: function ($scope, $element, $attrs, $ctrl) {

    }
  };
}

angular
  .module('app', [])
  .directive('fooDirective', fooDirective)
  .controller('FirstCtrl', FirstCtrl);

We have FirstCtrl which is passed into the Angular module, and then we reference it inside our Directive using controller: 'FirstCtrl'. This is great, however we can make this Controller lookup completely dynamic very easily!

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

Let’s add another Controller and call it SecondCtrl:

function SecondCtrl() {
  this.name = 'Second Controller';
}

Dynamic Controller lookups

The next step is changing the Directive to allow the Controller lookup value to be dynamic. This is done using the undocumented name property:

function fooDirective() {
  return {
    ...
    name: 'ctrl',
    ...
  };
}

What does name: [value] do? It allows us to pass value through an attribute into the Directive which we can use as a dynamic String for looking up Controllers in the module. The value of name becomes the attribute we bind to the Directive. For instance our fooDirective element will have an attribute ctrl.

If assigning a Controller, we would simply declare it as an attribute:

<foo-directive ctrl="FirstCtrl"></foo-directive>

Pretty simple, right?

It won’t work just yet, as our Directive controller: 'FirstCtrl' is still referencing wrong. We can actually change the 'FirstCtrl' value to '@' and it’ll work perfectly.

function fooDirective() {
  return {
    ...
    name: 'ctrl',
    controller: '@',
    ...
  };
}

All together now:

function FirstCtrl() {
  this.name = 'First Controller';
}

function SecondCtrl() {
  this.name = 'Second Controller';
}

function fooDirective() {
  return {
    scope: {},
    name: 'ctrl',
    controller: '@',
    controllerAs: 'foo',
    template: '<div>{{ foo.name }}</div>',
    link: function ($scope, $element, $attrs, $ctrl) {

    }
  };
}

angular
  .module('app', [])
  .directive('fooDirective', fooDirective)
  .controller('FirstCtrl', FirstCtrl)
  .controller('SecondCtrl', SecondCtrl);

Then we can add the same Directive again, passing in another Controller name as a String:

<foo-directive ctrl="FirstCtrl"></foo-directive>
<foo-directive ctrl="SecondCtrl"></foo-directive>

This also plays extremely nicely with the controllerAs syntax inside our Directive’s template, we don’t need to change anything!

Live output:

Inside the link function we also get the fourth argument (which I alias as $ctrl) given to us, with the correct instance of each dynamically assigned Controller:

function fooDirective() {
  return {
    scope: {},
    name: 'ctrl',
    controller: '@',
    controllerAs: 'foo',
    template: '<div>{{ foo.name }}</div>',
    link: function ($scope, $element, $attrs, $ctrl) {
      console.log($ctrl.name);
    }
  };
}

Thank you for reading!

Learn Angular the right way.

The most complete guide to learning Angular ever built.
Trusted by 82,951 students.

Todd Motto

with Todd Motto

Google Developer Expert icon Google Developer Expert

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover