Angular 指令依赖注入 - TypeScript

Posted

技术标签:

【中文标题】Angular 指令依赖注入 - TypeScript【英文标题】:Angular directive dependency injection - TypeScript 【发布时间】:2015-08-28 21:48:24 【问题描述】:

似乎有很多方法可以在 TypeScript 中创建 Angular 指令。我见过的最简洁的方法是使用静态工厂函数:

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());

但是如果我需要注入一些东西,我不确定该怎么做。假设我想要 $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());

正如您在上面看到的,我不确定如何调用 myDirective 构造函数并传入 $timeout。

【问题讨论】:

为什么要在 Angular 之上分层构建自己的模块?您从这层额外的复杂性、额外的全局状态和语义重复中获得什么好处?。 【参考方案1】:

只需将$timeout 指定为工厂构造函数参数并传递即可。

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

【讨论】:

就这么简单!谢谢!【参考方案2】:

虽然有一个公认的答案,但我想给我两分钱.. 前段时间我在指令和过滤器(Angular 向过滤器工厂注册)也遇到了同样的问题,所以我决定围绕标准类型定义构建一个小型库,允许我编写类似这样的东西(受控和模板代码是省略):

@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);
    


为了实现这一点,我必须自定义 TypeScript 代码发射器,现在它会生成接口元数据(ng.IQService 在运行时可用并映射到构造函数数组中的'$q');元数据由@Directive 装饰器使用,该装饰器在应用程序模块中使用this code 注册指令。 您可以查看示例应用程序代码here。

【讨论】:

【参考方案3】:

我遇到了同样的问题,并通过实现一个名为“ComponentRegistrator”的 Util 类解决了这个问题(灵感来自 cmets 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;
        
    

这可以例如用法如下:

/// <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);

注意构造函数中的return thisstatic $inject。您必须按照 PSL 的描述指定注入:

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

这样可以避免工厂方法的重复,并且可以始终使用相同的模式...

【讨论】:

【参考方案4】:

在我看来,稍微简单一点:

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

【讨论】:

以上是关于Angular 指令依赖注入 - TypeScript的主要内容,如果未能解决你的问题,请参考以下文章

Angular 1.5 组件依赖注入

Angular依赖注入机制与服务的作用范围

在自定义验证器指令中注入 ngControl,导致循环依赖

Angular2开发笔记

仅在需要时注入模块依赖项(如插件)

Angular依赖注入小解