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 这篇文章。