6

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.

1
  • 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?. Commented Jan 27, 2016 at 5:34

4 Answers 4

4

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;
    }
Sign up to request clarification or add additional context in comments.

1 Comment

As simple as that! Thanks!
0

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.

Comments

0

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 ...

Comments

0

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'];

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.