Angular 4 依賴注入教程之七 ValueProvider的使用

目錄

閱讀須知

本系列教程的開發環境及開發語言:segmentfault

基礎知識

ValueProvider 的做用

ValueProvider 用於告訴 Injector (注入器),但使用 Token 獲取依賴對象時,則返回 useValue 指定的值。

ValueProvider 的使用

const provider: ValueProvider = {provide: 'someToken', useValue: 'someValue'};

ValueProvider 接口

export interface ValueProvider {
  // 用於設置與依賴對象關聯的Token值,Token值多是Type、InjectionToken、
  // OpaqueToken的實例或字符串
  provide: any;
  // 設置注入的對象
  useValue: any;
  // 用於標識是否multiple providers,如果multiple類型,則返回與Token關聯的依賴
  // 對象列表
  multi?: boolean;
}

json-server 簡介

json-server 用於基於 JSON 數據快速地建立本地模擬的 REST API。

json-server 的安裝

npm install -g json-server

json-server 的使用

json-server --watch db.json

Angular CLI 代理配置

建立 proxy.conf.json 文件

{
  "/heros": {
    "target": "http://localhost:3000",
    "secure": false
  }
}

更新 package.json 文件

{
  "scripts": {
    "start": "ng serve --proxy-config proxy.conf.json",
  }
}

ValueProvider

介紹完基礎知識,咱們立刻進入正題。不知道你們是否還記得,在 "組件服務注入" 文章中提到的內容:

難道一切就這麼結束了,No! No!別忘記了咱們這節課的主題是介紹如何在組件中注入服務。在目前的 HeroComponent 組件,咱們的英雄列表信息是固定的,在實際的開發場景中,通常須要從遠程服務器獲取相應的信息。

接下來咱們將重構咱們的 HeroService 服務,從 API 接口中獲取英雄數據。要使用 Angular 的 Http 服務,首先須要在 AppModule 模塊中導入 HttpModule ,而後在 HeroService 類的構造函數中注入 Http 服務。

更新 HeroService 服務

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

import { LoggerService } from './logger.service';

@Injectable()
export class HeroService {
    constructor(private loggerService: LoggerService,
      private http: Http) { }

    apiUrl = 'http://localhost:4200/heros';

    getHeros(): Observable<Array<{ id: number; name: string }>> {
        this.loggerService.log('Fetching heros...');
        return this.http.get(this.apiUrl)
            .map(res => res.json())
    }
}

HeroService 服務中,咱們經過注入 Http 服務對象的 get() 方法發送 HTTP 請求,從而從 API 接口中獲取英雄的數據。

更新 HeroComponent 組件

import { Component, OnInit } from '@angular/core';
import { HeroService } from '../hero.service';
import { LoggerService } from '../logger.service';

@Component({
  selector: 'app-hero',
  template: `
    <ul>
      <li *ngFor="let hero of heros">
        ID: {{hero.id}} - Name: {{hero.name}}
      </li>
    </ul>
  `
})
export class HeroComponent implements OnInit {
  heros: Array<{ id: number; name: string }>;

  constructor(private heroService: HeroService,
    private loggerService: LoggerService) { }

  ngOnInit() {
    this.loggerService.log('Fetching heros...');
    this.heroService.getHeros()
      .subscribe(res => {
        this.heros = res;
      });
  }
}

更新完上述代碼,成功保存後,你會發現 http://localhost:4200/ 頁面,一片空白。這就對了,由於咱們尚未啓動本地的 json-server 服務器。接下來咱們來配置並啓動本地的 json-server 服務器:

建立 heros.json 文件

{
  "heros": [
    {"id":11,"name":"Mr. Nice"},
    {"id":12,"name":"Narco"},
    {"id":13,"name":"Bombasto"},
    {"id":14,"name":"Celeritas"},
    {"id":15,"name":"Magneta"}
 ]
}

啓動 json-server 服務器

json-server --watch heros.json

當成功啓動 json-server 服務器,在命令行中,你將看到如下輸出信息:

\{^_^}/ hi!

Loading heros.json
Done

這表示本地 json-server 已經成功啓動,此時從新刷新如下 http://localhost:4200/ 頁面,你將看到如下信息:

ID: 11 - Name: Mr. Nice
ID: 12 - Name: Narco
ID: 13 - Name: Bombasto
ID: 14 - Name: Celeritas
ID: 15 - Name: Magneta

程序終於又正常運行了,但注意到 HeroService 服務中,咱們經過如下方式定義 API 接口地址:

@Injectable()
export class HeroService {
  ...
  apiUrl = 'http://localhost:4200/heros';
}

這種方式有個問題,假設其它服務也要使用該地址,那麼就得按照一樣的方式去定義 API 接口地址。另外假設 API 接口地址須要更新,那就須要修改多個地方。針對上述問題,咱們可使用 ValueProvider 來解決問題。

使用 ValueProvider

@NgModule({
   ...,
   providers: [
    {
      provide: 'apiUrl',
      useValue: 'http://localhost:4200/heros'
    }
   ],
  bootstrap: [AppComponent]
})
export class AppModule { }

更新 HeroService 服務

@Injectable()
export class HeroService {
    constructor(private loggerService: LoggerService,
        private http: Http,
        @Inject('apiUrl') private apiUrl) { }

    getHeros(): Observable<Array<{ id: number; name: string }>> {
        this.loggerService.log('Fetching heros...');
        return this.http.get(this.apiUrl)
            .map(res => res.json())
    }
}

HeroService 類的構造函數中,咱們經過 @Inject('apiUrl') 方式,注入 apiUrlToken 對應的依賴對象,即 'http://localhost:4200/heros' 。爲何不能使用 private apiUrl: 'apiUrl' 的方式,但願讀者好好回憶一下,以前咱們介紹過的相關內容。

以上代碼成功運行後,在 http://localhost:4200/ 頁面,咱們將看到預期的結果:

ID: 11 - Name: Mr. Nice
ID: 12 - Name: Narco
ID: 13 - Name: Bombasto
ID: 14 - Name: Celeritas
ID: 15 - Name: Magneta

我有話說

爲何在構造函數中,非 Type 類型的參數只能用 @Inject(Something) 的方式注入 ?

由於 Type 類型的對象,會被 TypeScript 編譯器編譯。即咱們經過 class 關鍵字聲明的服務,最終都會編譯成 ES5 的函數對象。

在構造函數中,Type 類型的參數能用 @Inject(Type) 的方式注入麼?

Type 類型的參數也能使用 @Inject(Type) 的方式注入,具體以下:

constructor(@Inject(Http) private http) { }

一樣也可使用如下方式:

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

第一種方式雖然能夠正常編譯,但 IDE 會有以下的提示信息:

[ts] Parameter 'http' implicitly has an 'any' type.

第二種方式,雖然 Angular 內部會合並 design:paramtypes 與 parameters 內的 metadata 信息,但本人以爲是有點冗餘了。 總而言之,若果是 Type 類型的參數,推薦使用下面的方式:

constructor(private http: Http) { }

如有興趣瞭解 Inject 裝飾器的詳細信息,請參考 Angular 2 Inject 這篇文章。

相關文章
相關標籤/搜索