How to load data before your Angular app starts

Using APP_INITIALIZER

Do you have to load configuration files to set some application properties or to load some data before the application starts? Let's see how to do it in Angular. Before we get to that, here's a quick overview of how Angular application bootstraps in 6 steps:

Bootstraping process

  1. Load index.html
  2. Load Angular, Other Libraries, and App Code
  3. Execute main.ts File
  4. Load App Module
  5. Load App Component
  6. Process Template

The HTML file has <app-root></app-root> tag, where Angular injects our application.

Angular is based on modules and DI (Dependecy Injection) system. So in main.ts we have bootstrapModule() that takes the main application module as an argument and creates an instance of it for a given platform:

main.ts

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

App module (as any other Angular module) contains Declarations, Imports, Providers, but also entry component, which is a default AppComponent.

bootstrap: [AppComponent]

The component is then loaded and rendered on the page. Now that we understand how the process looks like, let's return back to the main topic. How to load data before the app starts? Given the steps listed at the beginning, we could say we have to interfere between the steps 4 and 5.

  • (4) Load App Module
  • --> use APP_INITIALIZER token in the providers array
  • (5) Load App Component

APP_INITIALIZER token

It is a DI token that you can use to provide one or more initialization functions. In the app.module.ts we have to provide it in the following way:

import { NgModule, APP_INITIALIZER } from '@angular/core';
// other imports...

@NgModule({
 imports: [BrowserModule],
 declarations: [AppComponent],
 bootstrap: [AppComponent],
 providers: [{
   provide: APP_INITIALIZER,
   useFactory: () => appConfigFactory
   deps: [AppInitService],
   multi: true
  }]
 })
export class AppModule {}

In the providers array we pass in APP_INITIALIZER token and we define a function called appConfigFactory (name is up to you of course) in useFactory property. The deps array could be empty, but you can pass any service you need here. To be more precise, a service that the factory function depends on. multi: true means we can define more app initializers.

This is how the token is defined in the source code of Angular:

export const APP_INITIALIZER =
    new InjectionToken<ReadonlyArray<() => Observable<unknown>| Promise<unknown>| void>>(
        'Application Initializer');

This tells us we have to return a Promise or an Observable.

Factory (initialization) function

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.

A basic example of the factory function described above can look like this. (Note the function could be also defined directly in the app.module.ts file and not in a separate file)

import { AppInitService } from './../services/app-init.service';

export function appConfigFactory(appInitService: AppInitService) {
  return (): Promise<any> => {
    return appInitService.init()
  }
}

Please also notice the AppInitService that we defined in the deps array is used in the factory function, where we call an 'init()` method from it. There you could implement any logic you need. For example you could use httpClient to load a JSON file with some data to configure your app.

Conclusion

We learned how to utilize APP_INITIALIZER token and its initialization functions in the module in order to perform some steps before our application component is initialized and rendered. This is a more elegant way than handling a logic/loading files directly in the components and component templates for example.

Resources/examples:

Angular docs

Compile-time vs. Runtime configuration of your Angular App

Bootstrapping an Application in Angular