Angular directive dependency injection - TypeScript

There seem to be several ways to create Angular directives in TypeScript. The easiest way I've seen is to use the 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 enter something. Let's say I need $ 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 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 myDirective contructor and pass in $ timeout.

+6
source share
4 answers

Just specify $timeout as an argument to the factory constructor function and skip it.

  static factory(): ng.IDirectiveFactory { var directive: ng.IDirectiveFactory = ($timeout:ng.ITimeoutService) => new myDirective($timeout); directive.$inject = ["$timeout"]; return directive; } 
+4
source

Although there is an accepted answer, I would like to give two cents. I once had the same problem with the directive, but also with filters (which Angular registers with filter factories), so I decided to create a small library around standard type definitions, which allowed me to write something like this (controlled and template code 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); } } 

To make this possible, I had to configure the TypeScript code emitter, and now it creates interface metadata (that ng.IQService is available at runtime and maps to '$q' in the constructor array); metadata is used by the @Directive decorator, which registers a directive in the application module with this code . You can see an example of application code here .

0
source

I ran into the same problem 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 it can be, for example, can 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); } 

Notice the return this in the constructor and static $inject . You will need to specify the injection as described in the PSL:

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

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

0
source

A 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']; 
0
source

Source: https://habr.com/ru/post/989035/


All Articles