下图是 SAP 电商云 Spartacus UI 中的一段和 Site Context 解决逻辑相干的代码:
export const contextServiceProviders: Provider[] = [ BaseSiteService, LanguageService, CurrencyService, { provide: APP_INITIALIZER, useFactory: initializeContext, deps: [ConfigInitializerService, SiteContextRoutesHandler], multi: true, },];
留神第 40 行的标记位,multi
,默认值为 false,这意味着在代码后注册的 initializer 的实现,会笼罩掉先注册的 initializer provider 实现。换言之,这种状况下,APP_INITIALIZER 的 provider 只能有一个。
如果 multi: true
被设置,那么新的提供者会被增加到之前注册的提供者中,使得一个令牌的提供者不止一个。当这个令牌被调用时,angular会执行所有这些令牌。
[外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-t9CQg0dA-1657529948839)(https://upload-images.jianshu...)]
因而,开发人员能够应用 multi: true
来创立 multi Provider令牌。这意味着咱们能够创立多个函数/服务,并在初始化期间调用它。
做一个测试,别离创立两个 initializer 的实现:
export function initializeApp2() { return (): Promise<any> => { return new Promise((resolve, reject) => { console.log(`initializeApp2 called`); setTimeout(() => { console.log(`initializeApp2 Finished`); resolve(); }, 2000); }); };}
而后应用 multi:true 给同一个 Injection Token APP_INITIALIZER
注册两个不同的 provider:
providers: [ AppInitService, { provide: APP_INITIALIZER,useFactory: initializeApp1, deps: [AppInitService], multi: true}, { provide: APP_INITIALIZER,useFactory: initializeApp2, multi: true} ],
最初运行时,这两个 initializers 会同时失去触发。
另外,这个例子应用了 useFactory
为 Injection Token 注册 provider.
一个例子:
const WINDOW = new InjectionToken<Window>('A reference to the window object', { factory: () => window,});
下面的例子,应用工厂函数作为提供者来设置 InjectionToken,就如同它是在应用程序的根注入器中显式定义的一样。 当初咱们能够在应用程序的任意地位应用它:
@Component({ selector: 'my-app'})export class AppComponent { constructor(@Inject(WINDOW) window: Window) {}}
咱们能够应用 inject
函数,在工厂函数的执行环境里,来获取其余提供者的援用。 让咱们看另一个例子:
import { inject, InjectionToken } from '@angular/core';import { ActivatedRoute } from '@angular/router';export type TimespanProvider = Observable<string>;export const TIMESPAN = new InjectionToken('Subscribe to timespan query param', { factory() { const activatedRoute = inject(ActivatedRoute); return activatedRoute.queryParams.pipe( pluck('timespan'), filterNil(), distinctUntilChanged() ); },});
下面的例子,咱们注入 ActivatedRoute,应用 inject
取得其 provider 实例,并为时间跨度查问参数返回一个 observable. 将 Observable<String>
导出成新的类型别名:TimespanProvider
.
这个 Injection Token 的注入例子:
@Component({ selector: 'app-home'})export class HomeComponent implements OnInit { constructor(@Inject(TIMESPAN) private timespan$: TimespanProvider) {} ngOnInit() { this.timespan$.pipe(untilDestroyed(this)).subscribe(console.log); }}
另一个例子:咱们有一个 ThemeService 实现类,将用户以后的 Theme 值裸露进来:
@Injectable({ providedIn: 'root' })export class ThemeService { private theme = new Subject<string>(); theme$ = this.theme.asObservable(); setTheme(theme: string) { this.theme.next(theme); }}
Component 读取以后 Theme 的值:
@Component({ selector: 'app-hello', template: `<h1>{{ theme$ | async }}</h1>`})export class HelloComponent { theme$: Observable<string>; constructor(private themeService: ThemeService) {} ngOnInit() { this.theme$ = this.themeService.theme$; } }
采取 Injection Token 的实现形式:
export type ActiveThemeProvider = Observable<string>;export const ACTIVE_THEME = new InjectionToken<ActiveThemeProvider>('Active theme', { factory() { return inject(ThemeService).theme$; }});
首先定义一个新的 Type
,而后应用 factory 办法,返回 Observable<string>
类型的后果。