在angular2服務中注入服務

來源:http://kittencup.com/javascript/2015/11/11/%E5%9C%A8angular2%E6%9C%8D%E5%8A%A1%E4%B8%AD%E6%B3%A8%E5%85%A5%E6%9C%8D%E5%8A%A1.html




如果你關注我們的文章 Angular2中的依賴注入,你知道DI系統在Angular中是如果運作的,它利用在我們代碼上通過註解添加metadata來獲取所有關於依賴的信息來解決我們的依賴關係

Angular 2 應用基本上可以用任何語言編寫。只要它以某種方式編譯成JavaScript,當我們使用Typescript編寫我們應用時,我們使用decorator來給我們代碼添加metadata,有時,我們甚至忽略一些decorator,單純依靠類型註釋。然而,事實證明,當涉及到DI,我們可能注入依賴到服務時遇到意外的行爲。

本文討論了這個意外的問題,爲什麼它存在,以及如何解決。

注入服務依賴

比方說我們有一個簡單的Angular 2 組件有一個DataService依賴,它可能是這個樣子:

@Component({
  selector: 'my-app'})@View({
  directives: [NgFor],
  template: `
    <ul>
      <li *ng-for="#item in items"></li>
    </ul>
  `})class AppComponent {
  items:Array<any>;
  constructor(dataService: DataService) {
    this.items = dataService.getItems();
  }}

另一方面 DataService 是一個簡單的類(因爲它在Angualr2是一個服務),它提供了一個方法返回一些items

class DataService {
  items:Array<any>;

  constructor() {
    this.items = [
      { name: 'Christoph Burgdorf' },
      { name: 'Pascal Precht' },
      { name: 'thoughtram' }
    ];
  }

  getItems() {
    return this.items;
  }}

當然,爲了能使用DataService類型,我們必須爲injector添加這個provider,當引導我們的應用時可以這樣做,通過傳遞一個provider給boostrap();

bootstrap(AppComponent, [DataService]);

到現在爲止沒有什麼新的內容,如果這對你來說是新內容,你可能需要先閱讀我們的Angular2中的依賴注入文章。

那麼問題在那裏呢? 這個問題發生在當我們試圖注入一個依賴進我們的服務,比如,我們可以使用Http在我們的DataService裏從遠程服務器獲取數據,讓我們快速實現這個。首先,我們需要爲injector提供一個provider,讓DataService知道有關http。

import {HTTP_PROVIDERS} from 'angular2/http';...bootstrap(AppComponent, [HTTP_PROVIDERS, DataService]);

Angular http模塊 暴露了 HTTP_PROVIDERS,它包含了所有的我們需要用到的http操作的provider,接下來,我們需要在我們的服務中注入這個實例

import {Http} from 'angular2/http';class DataService {
  items:Array<any>;

  constructor(http:Http) {
    ...
  }
  ...}

轟. 這個東西會爆炸。當我們在瀏覽器中運行這段代碼,我們會得到以下錯誤:

Cannot resolve all parameters for DataService(?). Make sure they all have valid type or annotations.

這錯誤基本上的意思是,它不能解決DataService的Http依賴,因爲Anuglar不知道該類型,因此 沒有provider可以用來解決該依賴,恩。。等等,我們沒有將變量類型提供給了constructor嗎?

不,我們提供了,不幸的是,這是不夠的,但是 在我們的AppComponent我們注入的DataService顯然是在正常工作,那在這裏有什麼問題?

在我們的Annotation和Decorator之間的區別文章,我們獲悉,decorator只是簡單的爲我們的代碼添加metadata,我們來看下編譯後的AppComponent的decorator

function AppComponent(myService) {
  ...}AppComponent = __decorate([
  Component({...}),
  View({...}), 
  __metadata('design:paramtypes', [DataService])], AppComponent);

我們可以清楚地看到,AppComponent類會被__decorate函數給包裝,裏面包裝了Component,View 和 paramtypes的metadata,

paramtypes 元素是一個告訴angular DI用來弄清楚,它必須返回一個什麼類型的實例

這看起來很不錯。讓我們來看看被編譯後的DataService,看看在那裏發生了什麼事情(也簡化了)。

DataService = (function () {
  function DataService(http) {
    ...
  }
  return DataService;})();

哎呀。顯然,在這裏沒有任何metadata。這是爲什麼?

當設置了emitDecoratorMetadata選項,TypeScript會生成metadata,然而,這並不意味着它會盲目的爲我們代碼的每個類或方法生成metadata,TypeScript只會對那些被decorator附加的類,方法,屬性或者構造函數參數來生成相應的metadata ,否則,會產生大量的未使用的metadata代碼,這不僅影響文件的大小,也對我們的應用程序運行產生影響。

這就是爲什麼AppComponent會生成metadata,而DataService 不生成,我們的AppComponent有一個decorator

強制生成metadata

那麼我們如果才能強制TypeScript爲我們生成metadata呢,我們可以做的一件事,就是用框架提供的Di decorator,正如我們在DI的其他文章中瞭解到,@Inject decorator用來要求某種類型的依賴

我們可以改變我們的DataService的成這樣的:

import {Inject} from 'angular2/core';import {Http} from 'angular2/http';class DataService {
  items:Array<any>;

  constructor(@Inject(Http) http:Http) {
    ...
  }
  ...}

問題解決。事實上,如果查看經過編譯後的代碼時會發現已生成需要的metadata

function DataService(http) {}DataService = __decorate([
  __param(0, angular2_1.Inject(Http)), 
  __metadata('design:paramtypes', [Http])], DataService);

我們基本上可以在我們代碼上做任何decorator,只要在class,或者構造函數參數上附加任何decorator,換一種說法,我們可以把 @Inject 移除,然後在這個類上使用別的decorator,因爲這將導致TypeScript爲構造函數參數生成metadata

當然。在一個類上使用一個decorator,來解決所有的問題,聽起來不是很合適。幸運的是,我們可以使用Angular自帶的另一個decorator。@Injectable 是一個用於Dart的metadata創建,在TypeScript,它沒有任何特殊含義,然而,事實證明是非常適合我們的用例.

我們所要做的就是導入它,把它放在我們的DataService

import {Injectable} from 'angular2/core';import {Http} from 'angular2/http';@Injectable()class DataService {
  items:Array<any>;

  constructor(http:Http) {
    ...
  }
  ...}

同樣,它只是強制TypeScript發射需要的matadata,這個decorator在這裏並沒有什麼特殊含義,這似乎是我們目前解決所示問題的最佳選擇


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章