If you are anything like me, chances are, you do not have a wide attention span, and you keep your memory space for more important things than to know what each of these tokens mean or how they work! So I setout to have a quick test to see which gets fired first, and what each expects. Through trial and error, and with zero regards to what goes under the hood.
Here is the StackBlitz project.
anchorThe documentation
Sucks! But here is what we are fishing for:
const APP_BOOTSTRAP_LISTENER: InjectionToken<((compRef: ComponentRef<any>) => void)[]>;
// we need to write a function that returns a function with this signature:
(compRef: ComponentRef<any>) => void
const APP_INITIALIZER: InjectionToken<readonly (() => void | Observable<unknown> | Promise<unknown>)[]>;
// we need to write a function that returns a function with this signature (I choose observable):
() => Observable<any>
const PLATFORM_INITIALIZER: InjectionToken<(() => void)[]>;
// we need to write a function that returns a function with this signature
() => void
The first two are provided in AppModule
and the third is mystically provided in platformBrowserDynamic
, don't know why, don't care. The tip was found buried in stackoverflow
anchorAdding the tokens
Adding the tokens first, then writing the functions later, so that I don't lose my sanity. In AppModule
:
@NgModule({
imports: [BrowserModule, HttpClientModule, CommonModule],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [
{
provide: APP_BOOTSTRAP_LISTENER,
useFactory: bootstrapFactory, // will be created
multi: true,
deps: [InitService], // will be created to see if it works
},
{
provide: APP_INITIALIZER,
useFactory: initFactory, // will be created
multi: true,
deps: [InitService], // will be created to see if it works
},
],
})
export class AppModule {}
In main.ts
as an argument to platformBrowserDynamic
platformBrowserDynamic([
{
provide: PLATFORM_INITIALIZER,
useValue: platformInitFactory, // will be created
multi: true,
deps: [InitService] // will be created to see if it works
},
])
.bootstrapModule(AppModule)
.catch((err) => console.error(err));
anchorThe factories
Find them in /services/initialize.service.ts
file in Stackblitz above. The factories are simple functions that return functions with matching signatures, and sweet ol' console.log
for each step. Let's see which gets called first, and what it returns. Let me also inject a dummy service to see if it is at all usable:
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
export const initFactory = (init: InitService): (() => Observable<any>) => {
console.log('APP_INITIALIZER factory called', init);
return () => {
console.log('APP_INITIALIZER callback');
return of(true);
};
};
export const platformInitFactory = (a: InitService): (() => void) => {
console.log('PLATFORM_INITIALIZER factory called', a);
return () => {
console.log('PLATFORM_INITIALIZER callback');
};
};
export const bootstrapFactory = (x: InitService): ((c: ComponentRef<any>) => void) => {
console.log('APP_BOOTSTRAP_LISTENER factory called', x);
return (c: ComponentRef<any>) => {
console.log('APP_BOOTSTRAP_LISTENER Callback:', c.location.nativeElement);
};
};
@Injectable({
providedIn: 'root',
})
export class InitService {
// nothing yet, I want to see how I can utilize this
}
Running this in StackBlitz gets me the following
/*
PLATFORM_INITIALIZER factory called undefined
APP_INITIALIZER factory called InitService {}
APP_INITIALIZER callback
APP_BOOTSTRAP_LISTENER factory called InitService {}
APP_BOOTSTRAP_LISTENER Callback: HTMLElement...
*/
So,
- PLATFORM_INITIALIZER gets called first but has no visibility for dependencies
- The PLATFORM_INITIALIZER call back function was never called! I shall unleash my wrath upon it
- APP_INITIALIZER is fired next, it has access to dependencies, and it allows an asynchronous response
- APP_BOOTSTRAP_LISTENER is garnish, accesses both dependencies and the app component, I cannot think of a use case for it.
- Angular docs of these tokens pretty much sucks--but otherwise, they are awesome, don't hate me please!
anchorUse cases
The most useful one is obviously APP_INITIALIZER
, I can inject HTTP Client and load something from server upon first initialization, say, configurations?
The second use case I can think of is inject actual JS file from the index.html
and map it internally to a typescript
interface.
I will attempt to do both, on a different post. 👋🏼