Define AngularJS directive using TypeScript and $inject mechanism Define AngularJS directive using TypeScript and $inject mechanism angularjs angularjs

Define AngularJS directive using TypeScript and $inject mechanism


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


I prefer to specify a controller for the directive and solely inject the dependencies there.

With the controller and its interface in place, I strongly type the 4th parameter of the link function to my controller's interface and enjoy utilizing it from there.

Shifting the dependency concern from the link part to the directive's controller allows me to benefit from TypeScript for the controller while I can keep my directive definition function short and simple (unlike the directive class approach which requires specifying and implementing a static factory method for the directive):

module app {"use strict";interface IMyDirectiveController {    // specify exposed controller methods and properties here    getUrl(): string;}class MyDirectiveController implements IMyDirectiveController {    static $inject = ['$location', 'toaster'];    constructor(private $location: ng.ILocationService, private toaster: ToasterService) {        // $location and toaster are now properties of the controller    }    getUrl(): string {        return this.$location.url(); // utilize $location to retrieve the URL    }}function myDirective(): ng.IDirective {    return {        restrict: 'A',        require: 'ngModel',        templateUrl: 'myDirective.html',        replace: true,        controller: MyDirectiveController,        controllerAs: 'vm',        link: (scope: ng.IScope, element: ng.IAugmentedJQuery, attributes: ng.IAttributes, controller: IMyDirectiveController): void => {            let url = controller.getUrl();            element.text('Current URL: ' + url);        }    };}angular.module('myApp').    directive('myDirective', 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

Solution:

 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        ...      }    };  }  myDirective.$inject = ['toaster']; // THIS LINE