Angular APP_INITIALIZER
APP_INITIALIZER
to wbudowany w Angulara InjectionToken
.
Pod InjectionToken
można zarejestrować wartość, funkcję albo serwis. Token ten można wstrzyknąć do komponentu lub serwisu.
Przykład zdefiniowania MY_TOKEN
:
export const MY_TOKEN = new InjectionToken<string>('MY_TOKEN');
Zarejestrowanie wartości Hello
pod MY_TOKEN
:
@NgModule({
// (...)
providers: [{
provide: MY_TOKEN,
useValue: 'Hello',
}]
// (...)
})
export class AppModule { }
Wstrzyknięcie wartości MY_TOKEN
do serwisu oraz wyświetlenie w konsoli przeglądarki Hello
:
@Injectable()
export class MyService {
constructor(@Inject(MY_TOKEN) public value: string) {
console.log(value);
}
}
Natomiast, dzięki tokenowi APP_INITIALIZER
, możliwe jest wykonanie funkcji, lub zestawu funkcji, które zostaną wykonane przed uruchomieniem aplikacji (bootstraping).
Przykład
Prosty przykład wywołania dwóch funkcji przed startem aplikacji:
export function appInit1() {
return () => console.log('Hello from appInit1!');
}
export function appInit2() {
return () => console.log('Hello from appInit2!');
}
Parametr multi
pozwala na rejestrację dwóch lub więcej funkcji pod APP_INITIALIZER
:
@NgModule({
// (...)
providers: [{
provide: APP_INITIALIZER,
useFactory: appInit1,
multi: true
},
{
provide: APP_INITIALIZER,
useFactory: appInit2,
multi: true
}],
// (...)
})
export class AppModule { }
W konsoli przeglądarki pojawią się poniższe komunikaty:
Hello from appInit1!
Hello from appInit2!
Przykład z Promise
Do APP_INITIALIZER
można także przekazać funkcję, która zwróci Promise! Angular poczeka, aż wszystkie zwrócone Promise’y zostaną rozwiązane (resolved).
export function appInit() {
return () => new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Hello from appInit');
resolve();
}, 2000);
})
}
@NgModule({
// (...)
providers: [{
provide: APP_INITIALIZER,
useFactory: appInit,
multi: true
}],
// (...)
})
export class AppModule { }
W rezultacie, po 2 sekundach od wywołania funkcji appInit
, w konsoli zostanie wyświetlona wiadomość: Hello from appInit
.
Zaawansowany przykład
Do funkcji uruchamianej przed bootstrapem aplikacji możliwe jest wstrzyknięcie serwisu.
W poniższym przykładzie aplikacja frontendowa ściąga konfigurację wymaganą do poprawnego działania.
Na backendzie wystawiony jest plik conf.json, serwowany przez http-server.
Interface Configuration jest modelem danych z pliku conf.json, zawiera tylko pole name.
Serwis AppInitService
wywołuje żądanie typu GET na /api/conf.json
.
export interface Configuration {
name;
}
@Injectable()
export class AppInitService {
constructor(private httpClient: HttpClient) {
}
init(): Promise<Configuration> {
return this.httpClient.get<Configuration>('api/conf.json')
.toPromise();
}
}
export function appInit(appInitService: AppInitService) {
return () => appInitService.init().then(configuration => console.log(configuration));
}
@NgModule({
// (...)
providers: [
AppInitService,
{
provide: APP_INITIALIZER,
useFactory: appInit,
deps: [AppInitService],
multi: true
}]
// (...)
})
export class AppModule { }
Powyższy kod wyświetli w konsoli konfigurację z pliku conf.json.
{name: "Test App name"}
Implementacja w Angularze
Przyjrzyjmy się teraz, w jaki sposób APP_INITIALIZER
został zaimplementowany w samym Angularze.
W pliku application_init.ts
znajduje się definicja InjectionToken
.
export const APP_INITIALIZER = new InjectionToken<Array<() => void>>('Application Initializer');
Początek bootstrapowania aplikacji w Angular wygląda następująco:
platformRef#bootstrapModuleFactory()
return _callAndReportToErrorHandler(exceptionHandler, ngZone !, () => {
const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus);
initStatus.runInitializers(); // (1)
return initStatus.donePromise.then(() => {
this._moduleDoBootstrap(moduleRef); // (2)
return moduleRef;
});
W punkcie (1) w serwisie ApplicationInitStatus wywołana jest funkcja runInitializers. Po zakończeniu ApplicationInitStatus, Angular przeprowadza bootstrap komponentu.
ApplicationInitStatus#runInitializers()
runInitializers() {
// (...)
const asyncInitPromises: Promise<any>[] = [];
if (this.appInits) {
for (let i = 0; i < this.appInits.length; i++) {
const initResult = this.appInits[i]();
if (isPromise(initResult)) {
asyncInitPromises.push(initResult);
}
}
}
Promise.all(asyncInitPromises).then(() => { complete(); }).catch(e => { this.reject(e); });
// (...)
}
Metoda runInitializers
sprawdza wywołania, które zwróciły Promise i czeka, aż wszystkie funkcje zostaną zakończone (resolve).
Zastosowania
Do czego można zastosować APP_INITIALIZER
?
- obsługa powiadomień push z serwera (comety),
- pobranie konfiguracji np. CSRF token,
- monitorowanie aktywności użytkownika,
- keep alive,
- keycloak - polecam świetny wpis Michała Hoji na ten temat źródło.
Nawet, jeżeli w swojej aplikacji nie używamy APP_INITIALIZER
, sam Angular wykorzystuje go do poprawnego działania.
Przykłady użycia w Angularze:
- RouterModule, używany jest do poprawnej pracy Guardów;
- WorkerAppModule;
- ServiceWorkerModule;
- NgProbe. Angular 9 wprowadza nowy renderer Ivy tym samym mechanizm NgProbe przestanie działać.
-
SENIOR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 14 900 - 20 590 PLN brutto
B2B 19 680 - 27 220 PLN netto -
REGULAR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 11 300 - 15 900 PLN brutto
B2B 14 950 - 21 000 PLN netto -
ZOBACZ WSZYSTKIE OGŁOSZENIA
newsletter
techniczny
Podobne wpisy
Czy wiesz, że w Angular 17 została wprowadzona alternatywa dla *ngIf?
-
SENIOR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 14 900 - 20 590 PLN brutto
B2B 19 680 - 27 220 PLN netto -
REGULAR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 11 300 - 15 900 PLN brutto
B2B 14 950 - 21 000 PLN netto -
ZOBACZ WSZYSTKIE OGŁOSZENIA