将 angularjs 指令实现为 Typescript 中的类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23535994/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Implementing angularjs directives as classes in Typescript
提问by Walter
So after taking a look at some of the examples of angularjs directives in typescript, it seems most people agree to use functions instead of classes when implementing them.
因此,在查看了 typescript 中 angularjs 指令的一些示例之后,似乎大多数人在实现它们时都同意使用函数而不是类。
I would prefer to have them as a class and attempted to implement them as follows:
我更愿意将它们作为一个类并尝试按如下方式实现它们:
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");
}
}
}
Now this works fine. However, I need to have an isolated scope with some attributes and I'm struggling to find out how to include that in the class itself.
现在这工作正常。但是,我需要有一个具有某些属性的隔离范围,并且我正在努力找出如何将其包含在类本身中。
logic dictates that since I can have
逻辑规定,因为我可以
public restrict: string;
public templateUrl: string;
I should be able to have something like:
我应该能够有类似的东西:
public scope;
But I'm not sure if this is correct or how to carry on from there (i.e how to add the attributes to the scope).
但我不确定这是否正确或如何从那里继续(即如何将属性添加到范围)。
Anybody know how to solve this? (hopefully, without having to revert to a function if possible)
有谁知道如何解决这个问题?(希望,如果可能的话,不必恢复到函数)
Thanks,
谢谢,
回答by bingles
Creating directives as classes can be problematic since you still need to involve a factory function to wrap its instantiation. For example:
将指令创建为类可能会出现问题,因为您仍然需要使用工厂函数来包装其实例化。例如:
export class SomeDirective implements ng.IDirective {
public link = () => {
}
constructor() {}
}
What Doesn't Work
什么不起作用
myModule.directive('someDirective', SomeDirective);
Since directives are not invoked using 'new' but are just called as factory functions. This will cause problems on what your constructor function actually returns.
由于指令不是使用“new”调用的,而只是作为工厂函数调用。这将导致构造函数实际返回的内容出现问题。
What Does (with Caveats)
有什么作用(有警告)
myModule.directive(() => new SomeDirective());
This works fine provided you don't have any IoC involved, but once you start introducing injectables, you have to maintain duplicate parameter lists for your factory function and your directive contstructor.
如果您不涉及任何 IoC,这可以正常工作,但是一旦您开始引入可注入对象,您就必须为工厂函数和指令构造函数维护重复的参数列表。
export class SomeDirective implements ng.IDirective {
...
constructor(someService: any) {}
}
myModule.directive('someDirective', ['someService', (someService) => new SomeDirective(someService)]);
Still an option if that is what you prefer, but is important to understand how the directive registration is actually consumed.
如果这是您喜欢的,仍然是一个选项,但对于了解指令注册实际是如何使用的很重要。
An alternative approach
另一种方法
The thing that is actually expected by angular is a directive factory function, so something like:
angular 实际期望的是一个指令工厂函数,例如:
export var SomeDirectiveFactory = (someService: any): ng.IDirective => {
return {
link: () => {...}
};
}
SomeDirectiveFactory.$inject = ['someService']; //including $inject annotations
myModule.directive('someDirective', SomeDirectiveFactory);
This has the benefit of allowing the use of $inject annotations since angular needs it to be on the factory function in this case.
这样做的好处是允许使用 $inject 注释,因为在这种情况下,angular 需要它在工厂函数上。
You could always return an instance of your class from the factory function as well:
您也可以始终从工厂函数返回类的实例:
export var SomeDirectiveFactory = (someService: any): ng.IDirective => {
return new SomeDirective(someService);
}
SomeDirectiveFactory.$inject = ['someService']; //including $inject annotations
But really depends on your use case, how much duplication of parameter lists you are okay with, etc.
但实际上取决于您的用例,您可以接受多少重复的参数列表等。
回答by basarat
Assuming that what you have works without an islolated scope, the following should work with an isolated scope:
假设您拥有的内容在没有孤立作用域的情况下工作,以下内容应该适用于孤立作用域:
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");
}
}
}
回答by b091
Here is my proposal:
这是我的建议:
Directive:
指示:
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);
}
}
Module (registers directive...):
模块(寄存器指令...):
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 (adds inject and produce directive object):
装饰器(添加注入和生产指令对象):
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;
};
}
I thinking about moving module.directive(...)
, module.service(...)
to classes files e.g. StoryBoxDirective.ts
but didn't make decision and refactor yet ;)
我想动module.directive(...)
,module.service(...)
上课文件如StoryBoxDirective.ts
,但没有作出决定,并重构之中;)
You can check full working example here: https://github.com/b091/ts-skeleton
您可以在此处查看完整的工作示例:https: //github.com/b091/ts-skeleton
Directive is here: https://github.com/b091/ts-skeleton/blob/master/src/module/story/StoryBoxDirective.ts
指令在这里:https: //github.com/b091/ts-skeleton/blob/master/src/module/story/StoryBoxDirective.ts
回答by czlatea
Here finally i got working a directive as class plus inheritance. In derived directive I extend the scope plus define the templateUrl. You can override any methods from base directive . The keywas to return from constructor the instance of directive.Angularjs calls constructor without newkeyword. In this case thisis of type windowI wrapped few lines to check the instance type of thisand in case of window I create a new instance of directive. (See Activator class from sample below)
在这里,我终于得到了一个指令作为类加继承。在派生指令中,我扩展了范围并定义了 templateUrl。您可以覆盖 base 指令中的任何方法。的关键是从构造函数返回directive.Angularjs的实例调用构造函数没有新的关键字。在这种情况下,这是一个类型的窗口我裹着几行来检查的实例类型这和窗口的情况下,我创建指令的新实例。(请参阅下面示例中的 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)));
}
}
}