How to Resolve Promise or Observable Before App Load – APP_INITIALIZER Angular 12 Example

Using APP_INITIALIZER in Angular 12 with examples; In this tutorial, you will learn how to perform a specific task before loading or initializing the Angular application. You will get to know how we do it using the APP_INITIALIZER DI token. It allows performing Asynchronous operations before even loading App Module.

This Angular post is compatible with Angular 4 upto latest versions, Angular 7, Angular 8, Angular 9, Angular 10, Angular 11 & Angular 12

What is APP_INITIALIZER DI Token in Angular?

For controlling the application initialization, we have the APP_INITIALIZER dependency injection token in Angular, which takes a factory function as input, which ideally returns a Promise callback. We can deploy the Promises to return a response that could be remote or the static local conditional expression.

Initialization of the application does not get completed until a valid response is received. To handle Asynchronous tasks we can use Promises or Observables. It could be an error or success that decides the load behaviour of an application.

What’s New in Angular 12 for APP_INITIALIZER?

Angular team has introduced a much-awaited requirement to the APP_INITIALIZER DI token. Previously, only Promises can be returned as factory responses. But after a huge number of requests to support Observables, the support for Observables has been pushed.

Earlier we used to use toPromise() method to convert an Observable into a promise response. But the toPromise() function is going to be depreciated in RxJS v7 and completely removed from v8. Moving forward we will discuss how to deploy Observables in the APP_INITIALIZER factory.

When to use APP_INITIALIZER in Angular?

There is a number of use-cases of APP_INITIALIZER token in the Angular application. Generally, It is deployed to load some remote data or control the application startup behaviour for example:

  • During OAuth implementation, we can verify the validity of token/ JWT, if it’s invalid the user is redirected to the identity provider (IdP) to perform authentication and fetch a valid token.
  • To pre-load user configurations from a remote server like i18n translation, user profile details, authorization list etc.
  • Implementation of local conditional expressions, add custom delays to display splash screens etc.

How to use APP_INITIALIZER in Angular App?

Like any other provider, we can place it into the module’s provider array. In our application, we will create a separate module, that will in turn call a service method to return the resolver function. In this step by step tutorial, you will learn how to use Promises as well as Observables with examples to initialize Angular applications.

  • Step 1 – Setup Angular Application
  • Step 2 – Create Init Service
  • Step 3 – Create Init Module
  • Step 4 – Import Init Module in App Module
  • Step 5 – Using Promises in APP_INITIALIZER
  • Step 6 – Using Observables in APP_INITIALIZER

 

Step 1 – Setup Angular Application

If you already having an Angular project up and running, you can skip this step. Make sure you have updated the Angular CLI to v 12 by hitting the following command:

npm install -g @angular/cli

Now, head towards the terminal window and execute the following command to create a new Angular app:

ng new angular-init-control-app

Move, into the application directory:

cd angular-init-control-app

Step 2 – Create Init Service

To have a method returning Promise or Observable response, we will create a new service named my-init.service.ts under the services folder. You can execute the following generate command in the terminal to create one for you:

ng g service services/my-init

It will create a service as this location ~src\app\services\my-init.service.ts. Update the service content with the following:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class MyInitService {

  constructor() { }

  initCheck() {
    return new Promise<void>((resolveFn, rejectFn) => {

      // Some condition to return resolve or reject response function
      if (true) {

        // App will load normally
        resolveFn();
      } else {

        // App will not load
        rejectFn();
      }

    })
  }

}

The initCheck() method returns a Promise object, that accepts two parameters to represent success or failure. The rejectFn() function is returned to depict error, while resolveFn() is returned on success.  You can return either of the function based on your conditional expression. Moreover, we will discuss in the upcoming section to call a remote API using Promises or RxJS Observable.

Step 3 – Create Init Module

Next, create a new Module to inject the APP_INITIALIZER DI token and accept the factory function using the useFactory. Also, inject the MyInitServer in the deps array to call its initCheck() method.

Create a new module at this location ~src/init.module.ts with the following content:

import { APP_INITIALIZER, NgModule } from '@angular/core';
import { MyInitService } from './services/my-init.service';

@NgModule({
  providers: [
      MyInitService,
      {
          provide: APP_INITIALIZER,
          useFactory:(myInitService:MyInitService)=>()=>myInitService.initCheck(),
          deps:[MyInitService],
          multi:true,
      }
  ]
})
export class InitModule { }

Step 4 – Import Init Module in App Module

Next, import the InitModule inside the imports array of AppModule of our Angular app. Open the app.module.ts file and update as shown below:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';

import { InitModule } from './init.module';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    InitModule // <-- Add InitModule Here
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Step 5 – Using Promises in APP_INITIALIZER

In this step, we will fetch data from a remote API call and return the Promise callback based on the received response. Here we will use the fetch() method to get the response.

Open the init.service.ts file and update the InitCheck() method as shown below:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class MyInitService {

  constructor() { }

  initCheck() {
    return new Promise<void>((resolveFn, rejectFn) => {

      fetch('https://jsonplaceholder.typicode.com/users/1')
        .then(res => {

          if (res.status == 200) {
            // App will load normally         
            resolveFn();
          } else {
            // App will not load
            alert('User not verified!');
            rejectFn();
          }

        });

    })
  }

}

Step 6 – Using Observables in APP_INTIALIZER

In this step, we will focus on How to use Observables in APP_INITILIZER providers. Open the my-init.service.ts file and update the initCheck() function as shown below:

import { Injectable } from '@angular/core';

import { Observable, of } from "rxjs";

@Injectable({
  providedIn: 'root'
})
export class MyInitService {
  constructor() { }
  initCheck() {
    return new Observable((subscriber) => {
      
      // Some condition to return resolve or reject response function
      if (true) {

        // App will load normally
        subscriber.complete();
      } else {

        // App will not load
        subscriber.error();
      }

    });
  }
}

Here you will notice, we are creating an Observable and retuning the complete() and error() callbacks similar to resolve and reject in case of Promises. This type of conditional expression works fine for local expression checks.

Using HTTP Responses in Observables

Now, we will fetch remote API responses from the Angular app. First, make sure you have imported the HttpClientModule in the app.module.ts file’s imports array.

import { HttpClientModule } from '@angular/common/http'
...
@NgModule({
   ...
  imports: [
    ...
    HttpClientModule 
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Next, open the my-init.service.ts file and update the initCheck() function to hit a remote API call using the Get method. Now, we are wrapping the Observable around the response.

One thing to note here is, we need to return complete() or error() to control application initialization.

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

import { Observable, of } from "rxjs";

@Injectable({
  providedIn: 'root'
})
export class MyInitService {

  constructor(private httpClient: HttpClient) { }

  initCheck() {

    return new Observable((subscriber) => {
      // HTTP Get call
      this.httpClient.get('https://jsonplaceholder.typicode.com/users/1').subscribe(res => {

        console.log(res);
        subscriber.complete();

      }, error => {
        
        console.log(error);
        subscriber.error();

      })
      
    });
  }
}

 

Conclusion

We discussed using APP_INTIALIZER by creating a new service and module. It will keep it abstracted from other application modules and easy to maintain. Using Promise we can wait for the application to load until the response is received.

After getting the response the resolve() and reject() in case or promises and complete() or error() for Observables callback methods can be used to control the application load and do other stuff like redirection or show error messages if unable to load remote configurations.

Leave a Reply

Your email address will not be published. Required fields are marked *