/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import {Observable} from 'rxjs'; import {Inject, Injectable, InjectionToken, Optional} from './di'; import {isObservable, isPromise} from './util/lang'; import {noop} from './util/noop'; /** * A [DI token](guide/glossary#di-token "DI token definition") that you can use to provide * one or more initialization functions. * * The provided functions are injected at application startup and executed during * app initialization. If any of these functions returns a Promise or an Observable, initialization * does not complete until the Promise is resolved or the Observable is completed. * * You can, for example, create a factory function that loads language data * or an external configuration, and provide that function to the `APP_INITIALIZER` token. * The function is executed during the application bootstrap process, * and the needed data is available on startup. * * @see `ApplicationInitStatus` * * @usageNotes * * The following example illustrates how to configure a multi-provider using `APP_INITIALIZER` token * and a function returning a promise. * * ``` * function initializeApp(): Promise { * return new Promise((resolve, reject) => { * // Do some asynchronous stuff * resolve(); * }); * } * * @NgModule({ * imports: [BrowserModule], * declarations: [AppComponent], * bootstrap: [AppComponent], * providers: [{ * provide: APP_INITIALIZER, * useFactory: () => initializeApp, * multi: true * }] * }) * export class AppModule {} * ``` * * It's also possible to configure a multi-provider using `APP_INITIALIZER` token and a function * returning an observable, see an example below. Note: the `HttpClient` in this example is used for * demo purposes to illustrate how the factory function can work with other providers available * through DI. * * ``` * function initializeAppFactory(httpClient: HttpClient): () => Observable { * return () => httpClient.get("https://someUrl.com/api/user") * .pipe( * tap(user => { ... }) * ); * } * * @NgModule({ * imports: [BrowserModule, HttpClientModule], * declarations: [AppComponent], * bootstrap: [AppComponent], * providers: [{ * provide: APP_INITIALIZER, * useFactory: initializeAppFactory, * deps: [HttpClient], * multi: true * }] * }) * export class AppModule {} * ``` * * @publicApi */ export const APP_INITIALIZER = new InjectionToken Observable| Promise| void>>( 'Application Initializer'); /** * A class that reflects the state of running {@link APP_INITIALIZER} functions. * * @publicApi */ @Injectable() export class ApplicationInitStatus { private resolve = noop; private reject = noop; private initialized = false; public readonly donePromise: Promise; public readonly done = false; constructor(@Inject(APP_INITIALIZER) @Optional() private readonly appInits: ReadonlyArray<() => Observable| Promise| void>) { this.donePromise = new Promise((res, rej) => { this.resolve = res; this.reject = rej; }); } /** @internal */ runInitializers() { if (this.initialized) { return; } const asyncInitPromises: Promise[] = []; const complete = () => { (this as {done: boolean}).done = true; this.resolve(); }; if (this.appInits) { for (let i = 0; i < this.appInits.length; i++) { const initResult = this.appInits[i](); if (isPromise(initResult)) { asyncInitPromises.push(initResult); } else if (isObservable(initResult)) { const observableAsPromise = new Promise((resolve, reject) => { initResult.subscribe({complete: resolve, error: reject}); }); asyncInitPromises.push(observableAsPromise); } } } Promise.all(asyncInitPromises) .then(() => { complete(); }) .catch(e => { this.reject(e); }); if (asyncInitPromises.length === 0) { complete(); } this.initialized = true; } }