Define AngularJS directive using TypeScript and $inject mechanism

Milko Lorinkov picture Milko Lorinkov · Nov 13, 2014 · Viewed 49.4k times · Source

Recently I started refactoring one of the Angular projects I am working on with TypeScript. Using TypeScript classes to define controllers is very convenient and works well with minified JavaScript files thanks to static $inject Array<string> property. And you get pretty clean code without splitting Angular dependencies from the class definition:

 module app {
  'use strict';
  export class AppCtrl {
    static $inject: Array < string > = ['$scope'];
    constructor(private $scope) {
      ...
    }
  }

  angular.module('myApp', [])
    .controller('AppCtrl', AppCtrl);
}

Right now I am searching for solution to handle similar case for the directive definition. I found a good practice to define the directives as function:

module directives {

  export function myDirective(toaster): ng.IDirective {
    return {
      restrict: 'A',
      require: ['ngModel'],
      templateUrl: 'myDirective.html',
      replace: true,
      link: (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes, ctrls) => 
        //use of $location service
        ...
      }
    };
  }


  angular.module('directives', [])
    .directive('myDirective', ['toaster', myDirective]);
}

In this case I am forced to define Angular dependencies in the directive definition, which can be very error-prone if the definition and TypeScript class are in different files. What is the best way to define directive with typescript and the $inject mechanism, I was searching for a good way to implement TypeScript IDirectiveFactory interface but I was not satisfied by the solutions I found.

Answer

tanguy_k picture tanguy_k · Mar 24, 2015

Using classes and inherit from ng.IDirective is the way to go with TypeScript:

class MyDirective implements ng.IDirective {
    restrict = 'A';
    require = 'ngModel';
    templateUrl = 'myDirective.html';
    replace = true;

    constructor(private $location: ng.ILocationService, private toaster: ToasterService) {
    }

    link = (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes, ctrl: any) => {
        console.log(this.$location);
        console.log(this.toaster);
    }

    static factory(): ng.IDirectiveFactory {
        const directive = ($location: ng.ILocationService, toaster: ToasterService) => new MyDirective($location, toaster);
        directive.$inject = ['$location', 'toaster'];
        return directive;
    }
}

app.directive('mydirective', MyDirective.factory());

Related answer: https://stackoverflow.com/a/29223360/990356