在 Typescript 中将 angularjs 指令实现为类

Posted

技术标签:

【中文标题】在 Typescript 中将 angularjs 指令实现为类【英文标题】:Implementing angularjs directives as classes in Typescript 【发布时间】:2014-06-25 12:06:17 【问题描述】:

因此,在查看了 typescript 中 angularjs 指令的一些示例之后,似乎大多数人都同意在实现它们时使用函数而不是类。

我希望将它们作为一个类并尝试按如下方式实现它们:

module directives
    
    export class search implements ng.IDirective
            
        public restrict: string;
        public templateUrl: string;

        constructor()
                    
            this.restrict = 'AE';
            this.templateUrl = 'directives/search.html';
        

        public link($scope: ng.IScope, element: JQuery, attributes: ng.IAttributes)
        
            element.text("Hello world");

        
    
 

现在这工作正常。但是,我需要一个具有某些属性的隔离范围,并且我正在努力找出如何将其包含在类本身中。

逻辑决定了,因为我可以拥有

public restrict: string;
public templateUrl: string;

我应该能够拥有类似的东西:

public scope;

但我不确定这是否正确或如何从那里继续(即如何将属性添加到范围)。

有人知道怎么解决吗? (希望,如果可能,不必恢复到函数)

谢谢,

【问题讨论】:

【参考方案1】:

将指令创建为类可能会有问题,因为您仍然需要使用工厂函数来包装其实例化。例如:

export class SomeDirective implements ng.IDirective 
    public link = () => 
    

    constructor() 

什么不起作用

myModule.directive('someDirective', SomeDirective);

因为指令不是使用“new”调用的,而是作为工厂函数调用的。这将导致您的构造函数实际返回的内容出现问题。

做什么(带有警告)

myModule.directive(() => new SomeDirective());

如果您不涉及任何 IoC,这可以正常工作,但是一旦您开始引入可注射,您必须为您的工厂函数和指令构造函数维护重复的参数列表。

export class SomeDirective implements ng.IDirective 
    ...
    constructor(someService: any) 


myModule.directive('someDirective', ['someService', (someService) => new SomeDirective(someService)]);

如果您愿意,这仍然是一个选项,但了解指令注册的实际使用方式很重要。

另一种方法

angular 实际期望的东西是一个指令工厂函数,所以类似于:

export var SomeDirectiveFactory = (someService: any): ng.IDirective => 
   return 
     link: () => ...
   ;

SomeDirectiveFactory.$inject = ['someService']; //including $inject annotations

myModule.directive('someDirective', SomeDirectiveFactory);

这具有允许使用 $inject 注释的好处,因为在这种情况下 Angular 需要它在工厂函数上。

您也可以始终从工厂函数返回您的类的实例:

export var SomeDirectiveFactory = (someService: any): ng.IDirective => 
   return new SomeDirective(someService);

SomeDirectiveFactory.$inject = ['someService']; //including $inject annotations

但实际上取决于您的用例,您可以使用多少重复的参数列表等。

【讨论】:

【参考方案2】:

假设您所拥有的东西在没有孤立作用域的情况下工作,以下内容应该适用于孤立作用域:

module directives
    
    export class search implements ng.IDirective
            
        public restrict = 'AE';
        public templateUrl = 'directives/search.html';
        public scope = 
            foo:'=',
            bar:'@',
            bas:'&'
        ;


        public link($scope: ng.IScope, element: JQuery, attributes: ng.IAttributes)
        
            element.text("Hello world");
        
    

【讨论】:

太棒了!谢谢巴萨拉特! 如何注册指令? 再想一想,这是否会引起麻烦,因为指令工厂不是“新的”,而是作为函数调用的?特别是构造函数不会返回类以外的东西吗? 我编写了一个实用程序,可用于在 TypeScript 中启用这种基于类的指令定义。 An example directive 可以找到解释here 要注册这个,@bingle 解释如下:myModule.directive("angEnter", () => new MyDirective);【参考方案3】:

这是我的建议:

指令:

import directive from '../../decorators/directive';

@directive('$location', '$rootScope')
export class StoryBoxDirective implements ng.IDirective 

  public templateUrl:string = 'src/module/story/view/story-box.html';
  public restrict:string = 'EA';
  public scope:Object = 
    story: '='
  ;

  public link:Function = (scope:ng.IScope, element:ng.IAugmentedJQuery, attrs:ng.IAttributes):void => 
    // console.info(scope, element, attrs, this.$location);
    scope.$watch('test', () => 
      return null;
    );
  ;

  constructor(private $location:ng.ILocationService, private $rootScope:ng.IScope) 
    // console.log('Dependency injection', $location, $rootScope);
  


模块(寄存器指令...):

import App from '../../App';
import StoryBoxDirective from './../story/StoryBoxDirective';
import StoryService from './../story/StoryService';

const module:ng.IModule = App.module('app.story', []);

module.service('storyService', StoryService);
module.directive('storyBox', <any>StoryBoxDirective);

装饰器(添加注入和生产指令对象):

export function directive(...values:string[]):any 
  return (target:Function) => 
    const directive:Function = (...args:any[]):Object => 
      return ((classConstructor:Function, args:any[], ctor:any):Object => 
        ctor.prototype = classConstructor.prototype;
        const child:Object = new ctor;
        const result:Object = classConstructor.apply(child, args);
        return typeof result === 'object' ? result : child;
      )(target, args, () => 
        return null;
      );
    ;
    directive.$inject = values;
    return directive;
  ;

我正在考虑将module.directive(...)module.service(...) 移至类文件,例如StoryBoxDirective.ts 但还没有做出决定和重构 ;)

您可以在此处查看完整的工作示例:https://github.com/b091/ts-skeleton

指令在这里:https://github.com/b091/ts-skeleton/blob/master/src/module/story/StoryBoxDirective.ts

【讨论】:

【参考方案4】:

最后我得到了一个指令作为类加继承。在派生指令中,我扩展了范围并定义了 templateUrl。 您可以覆盖基本指令中的任何方法 . key 是从构造函数返回指令的实例。Angularjs 调用没有 new 关键字的构造函数。在这种情况下,this 属于 window 类型 我换了几行来检查 this 的实例类型,如果是窗口,我会创建一个新的指令实例。 (参见下面示例中的 Activator 类)

module Realty.directives 
  export class BaseElementWithLabel implements ng.IDirective 
    public restrict = 'E';
    public replace = true;

    public scope = 
      label: '@',
      model: '=',
      icon: '@',
      readonlyElement: '=',
      remark: '@'
    

    constructor(extendedScope) 
      if (!(this instanceof Window)) 
        extendedScope = extendedScope || ;
        this.scope = angular.extend(this.scope, extendedScope);
      
    

    link(scope: ng.IScope, element: ng.IAugmentedJQuery, attributes: ng.IAttributes, controller, transclude) 
      scope['vm'] = ;
    
  


module Realty.directives 
  export class textareaWithLabel extends Realty.directives.BaseElementWithLabel implements ng.IDirective 
    templateUrl = 'directives/element-form/textarea-with-label-template.html';

    constructor() 
      super(
        rows: '@'
      );
      return Realty.Activator.Activate(this, textareaWithLabel, arguments);
    
  ;
;

module Realty 
  export class Activator 
    public static Activate(instance: any, type, arguments) 
      if (instance instanceof type) 
        return instance;
      

      return new(type.bind.apply(type, Array.prototype.concat.apply([null], arguments)));
    
  

【讨论】:

以上是关于在 Typescript 中将 angularjs 指令实现为类的主要内容,如果未能解决你的问题,请参考以下文章

Angularjs+Typescript 指令实现 $compile

在 AngularJS 上使用 Typescript 的指令中控制器的范围

TypeScript - 茉莉花 - Chutzpah - AngularJS

AngularJS TypeScript 指令链接函数

迈向angularjs2系列:typescript指南

将 AngularJS 对象作为 TypeScript 类注入(缺少方法)