Join the Stack Overflow Community
Stack Overflow is a community of 6.3 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

There seem to be a number of ways to create Angular directives in TypeScript. The neatest I've seen is to use a static factory function:

module app {
    export class myDirective implements ng.IDirective {
        restrict: string = "E";
        replace: boolean = true;
        templateUrl: string = "my-directive.html";

        link: ng.IDirectiveLinkFn = (scope: ng.IScope, el: ng.IAugmentedJQuery, attrs: ng.IAttributes) => {
        };

        static factory(): ng.IDirectiveFactory {
            var directive: ng.IDirectiveFactory = () => new myDirective();
            return directive;
        }
    }

    angular.module("app")
        .directive("myDirective", myDirective.factory());
}

But I'm not sure what to do if I need to inject something. Say I'd like $timeout:

module app {
    export class myDirective implements ng.IDirective {
        restrict: string = "E";
        replace: boolean = true;
        templateUrl: string = "my-directive.html";

        constructor(private $timeout: ng.ITimeoutService) {
        }

        link: ng.IDirectiveLinkFn = (scope: ng.IScope, el: ng.IAugmentedJQuery, attrs: ng.IAttributes) => {
            // using $timeout
             this.$timeout(function (): void {
             }, 2000);
        }

        static factory(): ng.IDirectiveFactory {
            var directive: ng.IDirectiveFactory = () => new myDirective(); // Uhoh! - What's goes here?
            directive.$inject = ["$timeout"];
            return directive;
        }
    }

    angular.module("app")
        .directive("myDirective", myDirective.factory());
}

As you can see above, I'm not sure how to call the myDirective contructor and pass in $timeout.

share|improve this question
    
Why are you layering your own modules on top of Angular? What benefit do you get from this extra layer of complexity, additional global state, and semantic duplication?. – yangmillstheory Jan 27 at 5:34
up vote 3 down vote accepted

Just specify $timeout as the factory constructor function argument and pass it through.

   static factory(): ng.IDirectiveFactory {
        var directive: ng.IDirectiveFactory = 
                       ($timeout:ng.ITimeoutService) => new myDirective($timeout); 
        directive.$inject = ["$timeout"];
        return directive;
    }
share|improve this answer
1  
As simple as that! Thanks! – user888734 Jun 12 '15 at 19:38
    
@user888734 You are welcome. :) – PSL Jun 12 '15 at 19:38

Although there is an accepted answer I'd like to give my two cents.. Sometime ago I had the same problem with directive, but also with filters (that Angular registers with filter factories), so I decided to build a small library around the standard type definitions that allowed me to write something like this (controlled and template code is omitted):

@Directive('userRank')
export class UserRankDirective implements ng.IDirective {

    controller = UserRankDirectiveController;
    restrict = 'A';
    template = template;
    //controllerAs: 'ctrl', set as default
    replace = true;
    scope = {
        user: '=userRank'
    }

    constructor($q: ng.IQService) {
        console.log('Q service in UserRankDirective:', $q);
    }

}

In order to make this possible I had to customize the TypeScript code emitter and now it produces interface metadata (that ng.IQService is available at runtime and mapped to '$q' in the constructor array); the metadata is used by the @Directive decorator that registers the directive in the application module with this code. You can give a look at the sample application code here.

share|improve this answer

I faced the same issue and solved it by implementing a Util class called "ComponentRegistrator" (inspired by the comments on this page):

/// <reference path="../../../Typings/tsd.d.ts"/>
module Common.Utils {
    "use strict";

    export class ComponentRegistrator {
        public static regService(app: ng.IModule, name: string, classType: Function) {
            return app.service(name, classType);
        }

        public static regController(app: ng.IModule, name: string, classType: Function) {
            var factory: Function = Component.reg(app, classType);
            return app.controller(name, factory);
        }

        public static regDirective(app: ng.IModule, name: string, classType: Function) {
            var factory: Function = Component.reg(app, classType);
            return app.directive(name, <ng.IDirectiveFactory>factory);
        }

        private static reg<T extends ng.IDirective>(app: ng.IModule, classType: Function) {
            var factory: Function = (...args: any[]): T => {
                var o = {};
                classType.apply(o, args) || console.error("Return in ctor missing!");
                return <T> o;
            };
            factory.$inject = classType.$inject || [];
            return factory;
        }
    }
}

And this can e.g. be used as follows:

/// <reference path="../../../Typings/tsd.d.ts"/>
///<reference path="../../Common/Utils/Component.ts"/>

module Sample {
    "use strict";

    class SampleDirective implements ng.IDirective {
        public static $inject: string[] = [];

        public templateUrl: string;
        public scope: {};
        public restrict: string;
        public require: string;
        public link: ng.IDirectiveLinkFn;

        constructor() {
            this.templateUrl = "/directives/sampleDirective.html";
            this.restrict = "A";
            this.scope = {
                element: "=",
            };
            this.link = this.linker;
            return this; // important!
        }

        private linker = (scope: IExpressionConsoleScope): void => {
            // ...
        };
    }

    ComponentRegistrator.regDirective(app, "directiveName", SampleDirective);
}

Note the return thisin the construcotor and the static $inject. You would have to specify the inject as described by PSL:

// ...
class SampleDirective implements ng.IDirective {
    public static $inject: string[] = ["$timeout"];
// ...
constructor(private $timeout:ng.ITimeoutService) {
// ...

This way the duplication of the factory method can be avoided and you can always use the same pattern ...

share|improve this answer

A little bit simpler, in my opinion:

export var SomeComponent = ($timeout: any): ng.IDirective => {
  return {
    controller,
    controllerAs: 'vm',
    restrict: 'E',
    templateUrl: 'components/someTemplate/someTemplate.html',
    scope: {
      someAttribute: '@'
    },
    link: {
      post: (scope, elem, attr, ctrl) => {
        console.log('Should see this here:', $timeout);
      }
    }
  };
}

SomeComponent.$inject = ['$timeout'];

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.