Hooking into the Angular Bootstrap process

This article explores how to use mechanisms that Angular provides to listen to framework initialization.

APP_BOOTSTRAP_LISTENER

You can register a listener for the APP_BOOTSTRAP_LISTENER token to listen to the Angular startup process, for example to see how it is used in Angular source code:

private _loadComponent(componentRef: ComponentRef<any>) :void {
    this.attachView(componentRef.hostView);
    this.tick();
    this.components.push(componentRef);
    // Get the listeners lazily to prevent DI cycles.
    const listeners =
        this._injector.get(APP_BOOTSTRAP_LISTENER,[]).concat(this._bootstrapListeners);
    listeners.forEach((listener) = > listener(componentRef));
  }
Copy the code

This _loadComponent() function is called when the program is initialized. By observing this function, you can see not only how a component is added to the program. It also knows that for each startup component, Angular executes a listener registered with the APP_BOOTSTRAP_LISTENER token and passes that startup object as a parameter to the listener function.

This means that we can use these hooks to listen for program startup and perform custom initialization logic, such as the Router module listening for startup and performing some initialization.

Since Angular passes the initialized startup component object as an argument to the callback function, we get the root component object ComponentRef like this:

import{APP_BOOTSTRAP_LISTENER, ... }from '@angular/core';
@NgModule({
  imports: [BrowserModule, ReactiveFormsModule, TasksModule],
  declarations: [AppComponent, BComponent, AComponent, SComponent, LiteralsComponent],
  providers: [{
    provide: APP_BOOTSTRAP_LISTENER, 
    multi: true, 
    useFactory: (a)= > {
      return (component: ComponentRef<any>) = > {
        console.log(component.instance.title);
      }
    }
  }],
  bootstrap: [AppComponent]
})
export class AppModule {
}
Copy the code

After running the code above, I went back to the official documentation, which described it like this:

All callbacks provided via this token will be called for every component that is bootstrapped. Signature of the callback: (componentRef: ComponentRef) => void

APP_INITIALIZER

Angular also provides hooks before application initialization. The Angular framework has the concepts of platform and Application. Angular instantiates platform and then Application at startup. A platform can have multiple applications. Platforms can have platform-browser, platform-service-worker, or platform-server, because the Angular framework wants to be cross-platform. So it has to instantiate a particular platform based on the current running environment. For platform and Application instantiation, see How to manually launch an Angular application), and then change detection and template rendering after initialization. The initialization steps are as follows:

if (this.appInits) {
	 for (let i = 0; i < this.appInits.length; i++) {
	   const initResult = this.appInits[i]();
	   if(isPromise(initResult)) { asyncInitPromises.push(initResult); }}}Copy the code

So, just as we did for the APP_BOOTSTRAP_LISTENER token, we also register the APP_INITIALIZER callback function. For example, the following code delays Angular initialization by 5 seconds:

{
  provide: APP_INITIALIZER,
  useFactory: (a)= > {
    return (a)= > {
      return new Promise((resolve, reject) = > {
        setTimeout((a)= > {
          resolve();
        }, 5000);
      });
    }
  },
  multi: true
}
Copy the code

Of course you can define multiple APP_INITIALIZER callbacks:

{
  provide: APP_INITIALIZER,
  useFactory: (a)= > {
    return (a)= > {
      return new Promise((resolve, reject) = > {
        setTimeout((a)= > {
          resolve();
        }, 5000);
      });
    }
  },
  multi: true
},
{
  provide: APP_INITIALIZER,
  useFactory: (a)= > {
    return (a)= > {
      return new Promise.resolve(2);
    }
  },
  multi: true
}
Copy the code

BootstrapModule

Another place to listen for the startup process is to use the bootstrapModule method:

platform.bootstrapModule(AppModule).then((module) = > {
  let applicationRef = module.injector.get(ApplicationRef);
  let rootComponentRef = applicationRef.components[0];
});
Copy the code

Here you can get the NgModuleRef object reference of the activated module and get ApplicationRef and ComponentRef from that object.