Angular 2中的依赖注入在ES5和ES6中的应用

23

这是一个基本的TypeScript/ES.next示例,使用装饰器进行依赖注入,并遵循框架手册建议的语法:

import {Component, Inject, Injectable, NgModule, OpaqueToken} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';

const CONSTANT = { value: 'constant' };
const CONSTANT_TOKEN = new OpaqueToken;
const CONSTANT_PROVIDER = { provide: CONSTANT_TOKEN, useValue: CONSTANT };

@Injectable()
class Service {
  constructor(@Inject(CONSTANT_TOKEN) constant) {
    console.log('Service constructor', constant);
  }
}

@Component({
  selector: 'app',
  template: '...',
  providers: [Service, CONSTANT_PROVIDER]
})
class AppComponent {
  constructor(@Inject(Service) service: Service, @Inject(CONSTANT_TOKEN) constant) {
    console.log('AppComponent constructor', service, constant);    
  }
}

@NgModule({
  imports: [BrowserModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
class AppModule {}

platformBrowserDynamic().bootstrapModule(AppModule);

在ES5中,它将被如何书写?

在未经转译的ES6 / ES2015中,将如何完成同样的事情?

在这些情况下,如何翻译InjectableInject修饰器?

该问题特别适用于具有类但可能使用requireSystem.import而不是ES6导入的实际ES6浏览器实现。


你为什么想要这样写呢?谷歌正在研究它,并且他们的支持将会是基于ES6的。只是好奇。 - Arnold Balliu
1
@ArnoldB Babel/TS/Dart的元语言工作流程并不适合每个项目。A2开发显然现在专注于TS和Dart,我并不确定原始JS在A2发布后是否会停止成为灰姑娘。了解您的选择永远不会有坏处。 - Estus Flask
我明白了。对于我个人而言,我喜欢TS,因为它是JS的超集,你可以编写任何有效的JS代码并运行。但是,更专注于您的问题,“@Injectable”装饰器的作用是:“@Injectable()将类标记为可由注入器实例化。一般来说,当尝试实例化未标记为@Injectable()的类时,注入器会报告错误。”我认为要翻译“@Injectable”,您需要翻译他们所说的注入器。 - Arnold Balliu
@ArnoldB 这并不完全正确,我刚刚弄清楚了关于Injectable的事情。它似乎使一个类能够通过TS类型注释使用隐式注入,并且对于仅依赖于Inject(如在JS中)的可注入对象是不必要的。我想我稍后会发布自己的答案。 - Estus Flask
2个回答

6

要使用Angular 2与ES5,您需要使用此脚本:

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/2.0.0-beta.3/angular2-all.umd.js"></script>

这提供了一个全局变量,其中包含所有Angular 2。现在,您可以编写ng.core.Component而不是@Component注释。构造函数的第一个参数是可注入项。

var Component = ng.core
  Component({
    selector: 'hello-cmp',
    template: 'Hello World!',
    viewProviders: [Service]
  .Class({
    constructor: [Service, function (service) { 
      ...
    }],
  });

并告诉注入器我们的服务参数是一个 Service 实例。

Component.parameters = [[new ng.core.Inject(Service)]];

以下示例展示了如何使用ES6与angular2:

import {Component} from 'angular2/core';
import {Service} from './example.service';

let componentAnnotation = new Component({
  selector: 'world-time',
  inputs: ['timeZones'],
  providers: [Service],
  template: `
    ...
  `
});
export class ComponentExample {
   constructor(service) {
    this._service = service;

   }
...

}

WorldTimeComponent.annotations = [componentAnnotation];
WorldTimeComponent.parameters = [[Service]];

这个plunkr中,您可以找到一个运行的ES6示例。
但是您可以通过使用Babel来使用装饰器。通过启用optional[]=es7.decorators(在webpack中),或者将您的配置设置为stage:1

1
ES2015不包括装饰器,问题是关于如何使用未转译的代码与Angular 2。我之前遇到过这种ES5语法,但我不确定它应该如何应用于ES6类。 - Estus Flask
我更新了我的答案,以更清晰地解释es5/es6之间的区别。 - muetzerich
谢谢。为了保持一致,请考虑问题中的示例,其中使用constant注入了Service,这可能是一个重要细节。您提到的示例使用的是2.0.0.beta-11版本,自那以后有很多破坏性变化。这里有一个使用建议语法的“es6”TS目标的plunker,它会抛出`“无法解析Service的所有参数:(?)”。 - Estus Flask
那个 angular2-all 脚本大小为 1.5 MB。对于一个 web 应用来说太多了。 - mohammad rostami siahgeli

2

Injectable 装饰器是 Angular 2 中 TypeScript 版本特有的。它通过 TypeScript 类型注解隐式地为 DI 注释类构造函数。对于已使用 Inject 注释的注入依赖项,它在 TS 中是冗余且在 JS 中不需要。

Angular 2 可注入对象(类和构造函数)应该在底层使用 annotationsparameters 静态属性进行注释。

annotations 是一个包含可注入类的 newed 装饰器的数组:

function SomeComponent(...) {}
SomeComponent.annotations = [new Componenent(...)];

parameters 是一个数组,其中包含构造函数参数的装饰器,每个元素是一个数组,其中包含相应构造函数属性的新装饰器列表(类似于 Angular 1.x 中的 $inject 属性显式注释):

function Service(someService, anotherService) {}
Service.parameters = [
  [new Inject(SomeService)],
  [new Inject(AnotherService), new Optional, new SkipSelf]
];

所有类装饰器都是从TypeDecorator扩展而来,这意味着它们可以被调用为函数。在这种情况下,使用所谓的DSL语法,允许将装饰器与Class助手函数链接起来:
var SomeComponent = Componenent(...).Class(...);

Class也可单独使用,它通过给定的定义对象构造一个新类,并允许对constructor方法进行注释(类似于Angular 1.x中的内联数组显式注释):

var SomeService = Class({
  constructor: [[new Inject(SomeService)], function (someService) {}]
});

Class helper在最新的框架版本中已经被弃用,应该使用ES5中的原始函数或第三方类帮助程序进行替换。装饰器支持直接链接类函数,Componenent(...)(ComponentClass).

使用System.import的Angular 2/4 ES6

一个示例

Promise.all([
  System.import('@angular/core'),
  System.import('@angular/platform-browser'),
  System.import('@angular/platform-browser-dynamic')
])
.then(([
  {Component, Inject, Injectable, Optional, NgModule, OpaqueToken},
  {BrowserModule},
  {platformBrowserDynamic}
]) => {

  const CONSTANT = { value: 'constant' };
  const CONSTANT_TOKEN = new OpaqueToken;
  const CONSTANT_PROVIDER = { provide: CONSTANT_TOKEN, useValue: CONSTANT };

  class Service {
    constructor(constant) {}
  }
  Service.parameters = [[new Inject(CONSTANT_TOKEN)]];

  class AppComponent {
    constructor(service, constant) {}
  }
  AppComponent.annotations = [new Component({
    selector: 'app',
    template: '...',
    providers: [Service, CONSTANT_PROVIDER]
  })];
  AppComponent.parameters = [[new Inject(Service)], [new Inject(CONSTANT_TOKEN)]];

  class AppModule {}
  AppModule.annotations = [new NgModule({
    imports: [BrowserModule],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
  })];

  platformBrowserDynamic().bootstrapModule(AppModule);

})
.catch((err) => console.error(err));

使用UMD模块和ng全局的Angular 2/4 ES5

一个示例:

var Class = ng.core.Class;
var Component = ng.core.Component;
var Inject = ng.core.Inject;
var Injectable = ng.core.Injectable;
var NgModule = ng.core.NgModule;
var OpaqueToken = ng.core.OpaqueToken;

var BrowserModule = ng.platformBrowser.BrowserModule;
var platformBrowserDynamic = ng.platformBrowserDynamic.platformBrowserDynamic;

var CONSTANT = { value: 'constant' };
var CONSTANT_TOKEN = new OpaqueToken;
var CONSTANT_PROVIDER = { provide: CONSTANT_TOKEN, useValue: CONSTANT };

// Class helper function that uses A1-flavoured inline array DI annotations
// and creates an annotated constructor
var Service = Class({
  constructor: [[new Inject(CONSTANT_TOKEN)], function (constant) {
    console.log('Service constructor', constant);
  }]
});
// can also be
// function Service(constant) {};
// Service.parameters = [[new Inject(...)], ...];

// when not being `new`ed, Component is a chainable factory that has Class helper method
var AppComponent = Component({
  selector: 'app', 
  template: '...',
  providers: [Service, CONSTANT_PROVIDER]
})
.Class({
  constructor: [
    [new Inject(Service)],
    [new Inject(CONSTANT_TOKEN)],
    function (service, constant) {
      console.log('AppComponent constructor', service, constant);
    }
  ]
});
// can also be
// function AppComponent(...) {};
// AppComponent.annotations = [new Component(...)];
// AppComponent.parameters = [[new Inject(...)], ...];

var AppModule = NgModule({
  imports: [BrowserModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
.Class({ constructor: function () {} });
// can also be
// function AppModule() {};
// AppModule.annotations = [new NgModule(...)];

platformBrowserDynamic().bootstrapModule(AppModule);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接