Выполнение кода до старта приложения через APP_INITIALIZER
Зачастую в Single Page Application необходимо запускать код перед запуском всего приложения. К примеру, для начала нам нужно отправить HTTP запрос для получения конфигурации, применить ее и только потом запускать бизнес логику. К счастью, в Angular нам предоставили такую возможность…
Angular предоставляет доступ к токену APP_INITIALIZER
. Его определение находится в https://github.com/angular/angular/blob/master/packages/core/src/application_init.ts
export const APP_INITIALIZER = new InjectionToken<Array<() => void>>('Application Initializer');
Да, эта возможность, пока, экспериментальная, но пугаться не стоит - работает стабильно.
В этом же файле, ниже, определен класс ApplicationInitStatus
который следит за выполнением APP_INITIALIZER
. Заметим, в конструктор внедряется массив функций, которые в свою очередь могут выполняться как синхронно, так и асинхронно. Эти функции возвращают объект Promise
, за которыми и следит этот класс, пока они не будут успешно выполнены, приложение не получит статус, что оно инициализировано. Так что можно быть уверенным, что данные будут вовремя и приложение не упадет с крахом. Но стоит помнить, что нагружать времязатратными операциями не стоит, ведь от этого хука зависит время выполнения первого старта.
И так, все предельно ясно. Как на счет практики?..
Да и тут все просто.
Определяется провайдер в коренном модуле (app.module.ts
) и только тут.
{provide: APP_INITIALIZER, useValue: () => promise, multi: true}]}
где promise
это любая наша функция.
На “живом примере” это будет выглядеть следующим образом.
// app.module.ts
export function loadConfig(config: AppConfig) => () => config.load()
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule,
appRoutes,
FormsModule,
HttpModule],
providers: [AuthService,
appRoutingProviders,
AppConfig,
{ provide: APP_INITIALIZER,
useFactory: loadConfig,
deps: [AppConfig],
multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
Не забывайте параметр multi
. Так как APP_INITIALIZER
является массивом функций, и этот параметр является обязательным для возможности добавления нового значения в этот массив.
Далее определить сервис AppConfig
и функцию load
, которую мы хотели бы вызвать при инициализации приложения.
// app.config.ts
@Injectable()
export class AppConfig {
private _config: any;
constructor(private http: Http) { }
load(): Promise<any> {
this._config = null;
return this.http
.get('REST_API_URL_FOR_LOAD_CONFIG')
.map((res: Response) => res.json())
.toPromise()
.then((data: any) => this._config = data)
.catch((err: any) => Promise.resolve());
}
get config(): any {
return this._config;
}
}
Важно, чтобы функция load
возвращала Promise
.