在angularcript中将angularjs指令实现为类

时间:2014-05-08 07:59:29

标签: angularjs typescript

因此,在看一下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;

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

有谁知道如何解决这个问题? (希望如果可能的话,不必恢复功能)

谢谢,

4 个答案:

答案 0 :(得分:23)

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

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

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

答案 1 :(得分:20)

假设您在没有islolated范围的情况下工作,以下内容应该适用于隔离范围:

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");
        }
    }
}

答案 2 :(得分:2)

以下是我的建议:

指令:

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

Decorator(添加inject和产生指令对象):

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

答案 3 :(得分:0)

最后,我得到了一个指令作为类加继承。在派生指令中,我扩展了范围并定义了templateUrl。 您可以覆盖base指令中的任何方法 。 是从构造函数返回的,指令的实例.Angularjs调用没有 new 关键字的构造函数。在这种情况下,的类型为窗口 我用几行来检查 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)));
    }
  }
}