W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
通過配置提供者,你可以把服務(wù)提供給那些需要它們的應(yīng)用部件。
依賴提供者會(huì)使用 DI 令牌來配置注入器,注入器會(huì)用它來提供這個(gè)依賴值的具體的、運(yùn)行時(shí)版本。
如果你把服務(wù)類指定為提供者令牌,那么注入器的默認(rèn)行為是用 ?new
?來實(shí)例化那個(gè)類。
在下面這個(gè)例子中,?Logger
?類提供了 ?Logger
?的實(shí)例。
providers: [Logger]
不過,你也可以用一個(gè)替代提供者來配置注入器,這樣就可以指定另一些同樣能提供日志功能的對(duì)象。
可以使用服務(wù)類來配置注入器,也可以提供一個(gè)替代類、一個(gè)對(duì)象或一個(gè)工廠函數(shù)。
當(dāng)使用提供者配置注入器時(shí),會(huì)將該提供者與依賴項(xiàng)注入令牌(或叫 DI 令牌)關(guān)聯(lián)起來。注入器允許 Angular 創(chuàng)建任何內(nèi)部依賴項(xiàng)的映射。DI 令牌會(huì)充當(dāng)該映射的鍵名。
依賴項(xiàng)值是一個(gè)實(shí)例,而這個(gè)類的類型用作查找鍵。在這里,注入器使用 ?HeroService
?類型作為令牌來查找 ?heroService
?。
heroService: HeroService;
當(dāng)你使用 ?HeroService
?類的類型來定義構(gòu)造函數(shù)參數(shù)時(shí),Angular 會(huì)注入與這個(gè) ?HeroService
?類令牌相關(guān)聯(lián)的服務(wù):
constructor(heroService: HeroService)
盡管許多依賴項(xiàng)的值是通過類提供的,但擴(kuò)展的 ?provide
?對(duì)象使你可以將不同種類的提供者與 DI 令牌相關(guān)聯(lián)。
類提供者的語法實(shí)際上是一種簡(jiǎn)寫形式,它會(huì)擴(kuò)展成一個(gè)由 ?Provider
?接口定義的提供者配置對(duì)象。 下面的代碼片段展示了 ?providers
?中給出的類會(huì)如何擴(kuò)展成完整的提供者配置對(duì)象。
providers: [Logger]
Angular 把這個(gè) ?providers
?值擴(kuò)展為一個(gè)完整的提供者對(duì)象,如下所示。
[{ provide: Logger, useClass: Logger }]
擴(kuò)展的提供者配置是一個(gè)具有兩個(gè)屬性的對(duì)象字面量:
provide
?屬性存有令牌,它作為一個(gè) key,在定位依賴值和配置注入器時(shí)使用。useClass
?—— 就像這個(gè)例子中一樣。 也可以是 ?useExisting
?、?useValue
?或 ?useFactory
?。 每一個(gè) key 都用于提供一種不同類型的依賴,我們稍后會(huì)討論。不同的類可以提供相同的服務(wù)。例如,以下代碼告訴注入器,當(dāng)組件使用 ?Logger
?令牌請(qǐng)求一個(gè) logger 時(shí),給它返回一個(gè) ?BetterLogger
?。
[{ provide: Logger, useClass: BetterLogger }]
如果替代類提供者有自己的依賴,那就在父模塊或組件的元數(shù)據(jù)屬性 ?providers
?中指定那些依賴。
[ UserService,
{ provide: Logger, useClass: EvenBetterLogger }]
在這個(gè)例子中,?EvenBetterLogger
?會(huì)在日志信息里顯示用戶名。 這個(gè) logger 要從注入的 ?UserService
?實(shí)例中來獲取該用戶。
@Injectable()
export class EvenBetterLogger extends Logger {
constructor(private userService: UserService) { super(); }
override log(message: string) {
const name = this.userService.user.name;
super.log(`Message to ${name}: ${message}`);
}
}
注入器需要提供這個(gè)新的日志服務(wù)以及該服務(wù)所依賴的 ?UserService
?對(duì)象。
要為類提供者設(shè)置別名,請(qǐng)?jiān)?nbsp;?providers
?數(shù)組中使用 ?useExisting
?屬性指定別名和類提供者。
在下面的例子中,當(dāng)組件請(qǐng)求新的或舊的記錄器時(shí),注入器都會(huì)注入一個(gè) ?NewLogger
?的實(shí)例。 通過這種方式,?OldLogger
?就成了 ?NewLogger
?的別名。
[ NewLogger,
// Alias OldLogger w/ reference to NewLogger
{ provide: OldLogger, useExisting: NewLogger}]
請(qǐng)確保你沒有使用 ?useClass
?來把 ?OldLogger
?設(shè)為 ?NewLogger
?的別名,因?yàn)槿绻@樣做它就會(huì)創(chuàng)建兩個(gè)不同的 ?NewLogger
?實(shí)例。
通常,編寫同一個(gè)父組件別名提供者的變體時(shí)會(huì)使用forwardRef,如下所示。
providers: [{ provide: Parent, useExisting: forwardRef(() => AlexComponent) }],
為簡(jiǎn)化你的代碼,可以使用輔助函數(shù) ?provideParent()
? 來把這個(gè)邏輯提取到一個(gè)輔助函數(shù)中。
// Helper method to provide the current component instance in the name of a `parentType`.
export function provideParent
(component: any) {
return { provide: Parent, useExisting: forwardRef(() => component) };
}
現(xiàn)在,你可以為組件添加一個(gè)更容易閱讀和理解的父提供者。
providers: [ provideParent(AliceComponent) ]
要為多個(gè)父類型指定別名(每個(gè)類型都有自己的類接口令牌),請(qǐng)配置 ?provideParent()
? 以接受更多的參數(shù)。
這是一個(gè)修訂版本,默認(rèn)值為 ?parent
?但同時(shí)也接受另一個(gè)父類接口作為可選的第二參數(shù)。
// Helper method to provide the current component instance in the name of a `parentType`.
// The `parentType` defaults to `Parent` when omitting the second parameter.
export function provideParent
(component: any, parentType?: any) {
return { provide: parentType || Parent, useExisting: forwardRef(() => component) };
}
接下來,要使用 ?provideParent()
?,請(qǐng)傳入第二參數(shù),這里是 ?DifferentParent
?。
providers: [ provideParent(BethComponent, DifferentParent) ]
要注入一個(gè)對(duì)象,可以用 ?useValue
?選項(xiàng)來配置注入器。 下面的提供者定義對(duì)象使用 ?useValue
?作為 key 來把該變量與 ?Logger
?令牌關(guān)聯(lián)起來。
[{ provide: Logger, useValue: SilentLogger }]
在這個(gè)例子中,?SilentLogger
?是一個(gè)充當(dāng)記錄器角色的對(duì)象。
// An object in the shape of the logger service
function silentLoggerFn() {}
export const SilentLogger = {
logs: ['Silent logger says "Shhhhh!". Provided via "useValue"'],
log: silentLoggerFn
};
常用的對(duì)象字面量是配置對(duì)象。下列配置對(duì)象包括應(yīng)用的標(biāo)題和 Web API 的端點(diǎn)地址。
export const HERO_DI_CONFIG: AppConfig = {
apiEndpoint: 'api.heroes.com',
title: 'Dependency Injection'
};
要提供并注入配置對(duì)象,請(qǐng)?jiān)?nbsp;?@NgModule()
? 的 ?providers
?數(shù)組中指定該對(duì)象。
providers: [
UserService,
{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }
],
可以定義和使用一個(gè) ?InjectionToken
?對(duì)象來為非類的依賴選擇一個(gè)提供者令牌。下列例子定義了一個(gè)類型為 ?InjectionToken
?的 ?APP_CONFIG
?。
import { InjectionToken } from '@angular/core';
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
可選的參數(shù) ?<AppConfig>
? 和令牌描述 ?app.config
? 指明了此令牌的用途。
接著,用 ?APP_CONFIG
?這個(gè) ?InjectionToken
?對(duì)象在組件中注冊(cè)依賴提供者。
providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]
現(xiàn)在,借助參數(shù)裝飾器 ?@Inject()
?,你可以把這個(gè)配置對(duì)象注入到構(gòu)造函數(shù)中。
constructor(@Inject(APP_CONFIG) config: AppConfig) {
this.title = config.title;
}
雖然 TypeScript 的 ?AppConfig
?接口可以在類中提供類型支持,但它在依賴注入時(shí)卻沒有任何作用。在 TypeScript 中,接口是一項(xiàng)設(shè)計(jì)期工件,它沒有可供 DI 框架使用的運(yùn)行時(shí)表示形式或令牌。
當(dāng)轉(zhuǎn)譯器把 TypeScript 轉(zhuǎn)換成 JavaScript 時(shí),接口就會(huì)消失,因?yàn)?nbsp;JavaScript 沒有接口。
由于 Angular 在運(yùn)行期沒有接口,所以該接口不能作為令牌,也不能注入它。
// Can't use interface as provider token
[{ provide: AppConfig, useValue: HERO_DI_CONFIG })]
// Can't inject using the interface as the parameter type
constructor(private config: AppConfig){ }
要想根據(jù)運(yùn)行前尚不可用的信息創(chuàng)建可變的依賴值,可以使用工廠提供者。
在下面的例子中,只有授權(quán)用戶才能看到 ?HeroService
?中的秘密英雄。授權(quán)可能在單個(gè)應(yīng)用會(huì)話期間發(fā)生變化,比如改用其他用戶登錄。
要想在 ?UserService
?和 ?HeroService
?中保存敏感信息,就要給 ?HeroService
?的構(gòu)造函數(shù)傳一個(gè)邏輯標(biāo)志來控制秘密英雄的顯示。
constructor(
private logger: Logger,
private isAuthorized: boolean) { }
getHeroes() {
const auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
this.logger.log(`Getting heroes for ${auth} user.`);
return HEROES.filter(hero => this.isAuthorized || !hero.isSecret);
}
要實(shí)現(xiàn) ?isAuthorized
?標(biāo)志,可以用工廠提供者來為 ?HeroService
?創(chuàng)建一個(gè)新的 logger 實(shí)例。
const heroServiceFactory = (logger: Logger, userService: UserService) =>
new HeroService(logger, userService.user.isAuthorized);
這個(gè)工廠函數(shù)可以訪問 ?UserService
?。你可以同時(shí)把 ?Logger
?和 ?UserService
?注入到工廠提供者中,這樣注入器就可以把它們傳給工廠函數(shù)了。
export const heroServiceProvider =
{ provide: HeroService,
useFactory: heroServiceFactory,
deps: [Logger, UserService]
};
useFactory
?字段指定該提供者是一個(gè)工廠函數(shù),其實(shí)現(xiàn)代碼是 ?heroServiceFactory
?。deps
?屬性是一個(gè)提供者令牌數(shù)組。 ?Logger
?和 ?UserService
?類都是自己類提供者的令牌。該注入器解析了這些令牌,并把相應(yīng)的服務(wù)注入到 ?heroServiceFactory
?工廠函數(shù)的參數(shù)中。通過把工廠提供者導(dǎo)出為變量 ?heroServiceProvider
?,就能讓工廠提供者變得可復(fù)用。
下面這兩個(gè)并排的例子展示了在 ?providers
?數(shù)組中,如何用 ?heroServiceProvider
?替換 ?HeroService
?
import { Component } from '@angular/core';
import { heroServiceProvider } from './hero.service.provider';
@Component({
selector: 'app-heroes',
providers: [ heroServiceProvider ],
template: `
<h2>Heroes</h2>
<app-hero-list></app-hero-list>
`
})
export class HeroesComponent { }
import { Component } from '@angular/core';
import { HeroService } from './hero.service';
@Component({
selector: 'app-heroes',
providers: [ HeroService ],
template: `
<h2>Heroes</h2>
<app-hero-list></app-hero-list>
`
})
export class HeroesComponent { }
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: