I recently decided to upgrade one of my many half-projects to the latest Angular (18) as the application was falling behind (14 or 15). In the process I discovered the APP_INIITALIZER functionality, which I've started playing with.
Here are some notes on my implementation of APP_INITIALIZER in Angular 18. Mostly for future me to refer back to!
I am (very) far from an expert in Angular, I'm sure plenty of Angular devs will read this and be able to suggest improvements (I'm all ears).
What is APP_INITIALIZER ?
APP_INITIALIZER is a token that allows the coder to run some code before the main initialization of the app.component of an Angular application.
It is setup in providers of the application to tell Angular to run some code before the main application.
How do I use it?
As part of my Angular migration to 18 I found myself adding a main.ts file, removing module files as I migrate to standalone Angular components. In that file I have an array of providers, that's where I define my APP_INITIALIZER token.
I'm using it to make sure the user status is set correctly ahead of the main application startup, checking if we have a user in local storage. Or a token we can use to auto login in the background.
bootstrapApplication(AppComponent, {
providers: [
appConfig.providers,
<full list of providers omitted>
{ provide: APP_INITIALIZER,
useFactory: init,
deps: [MyService, MyOtherService],
multi: true
},
]
})
.catch(err => console.error(err));
The important bits are the 'useFactory' and 'deps' properties.
useFactory defines the function to use before continuing to run app.component, deps defines what that function needs.
In my main.ts I created an init() function, this is what is referenced by the useFactory part of the APP_INITIALIZER declaration....
export function init(service: MyService, otherService: MyOtherService) {
return () => concat(service.initState(), otherService.doSomething());
}
Key thing to note here is that the functions called within that concat() function must return an Observable that completes. Don't just rely on observable.next(something) you must call observable.complete.
Angular will not continue to run the application until all observables provided complete.
Arguably it may be neater to wrap the 2 calls I have in my code with an InitService, but as it's such a small function I quite like having it next to the APP_INITIALIZER token that causes it to be called.