Angular 4 依賴注入教程之四 FactoryProvider的使用typescript
Angular 4 依賴注入教程之七 ValueProvider的使用json
Angular 4 依賴注入教程之八 InjectToken的使用bootstrap
本系列教程的開發環境及開發語言:segmentfault
ValueProvider 用於告訴 Injector (注入器),但使用 Token
獲取依賴對象時,則返回 useValue
指定的值。
const provider: ValueProvider = {provide: 'someToken', useValue: 'someValue'};
export interface ValueProvider { // 用於設置與依賴對象關聯的Token值,Token值多是Type、InjectionToken、 // OpaqueToken的實例或字符串 provide: any; // 設置注入的對象 useValue: any; // 用於標識是否multiple providers,如果multiple類型,則返回與Token關聯的依賴 // 對象列表 multi?: boolean; }
json-server 用於基於 JSON 數據快速地建立本地模擬的 REST
API。
npm install -g json-server
json-server --watch db.json
proxy.conf.json
文件{ "/heros": { "target": "http://localhost:3000", "secure": false } }
package.json
文件{ "scripts": { "start": "ng serve --proxy-config proxy.conf.json", } }
介紹完基礎知識,咱們立刻進入正題。不知道你們是否還記得,在 "組件服務注入" 文章中提到的內容:
難道一切就這麼結束了,No! No!別忘記了咱們這節課的主題是介紹如何在組件中注入服務。在目前的
HeroComponent
組件,咱們的英雄列表信息是固定的,在實際的開發場景中,通常須要從遠程服務器獲取相應的信息。
接下來咱們將重構咱們的 HeroService
服務,從 API 接口中獲取英雄數據。要使用 Angular 的 Http
服務,首先須要在 AppModule
模塊中導入 HttpModule
,而後在 HeroService
類的構造函數中注入 Http
服務。
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
接口中獲取英雄的數據。
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
來解決問題。
@NgModule({ ..., providers: [ { provide: 'apiUrl', useValue: 'http://localhost:4200/heros' } ], bootstrap: [AppComponent] }) export class AppModule { }
@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')
方式,注入 apiUrl
該 Token
對應的依賴對象,即 '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
類型的對象,會被 TypeScript
編譯器編譯。即咱們經過 class
關鍵字聲明的服務,最終都會編譯成 ES5
的函數對象。
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 這篇文章。