So, Angular APP_INITIALIZER uses a very simple rule. During app initialization, all the functions which are injected under this token will need to resolve or return something, before the application boots.
How will this be beneficial to you? It will make it possible for you to do all the things you want to do, before your Angular application loads. What are some of the things you can do?
Loading configurations from backend.
Detecting the user location and providing custom experience and even UI.
Redirecting users.
Detecting Ad blockers and preventing app load (Yeah, you must like this one)
You can do so much more, by using the Angular APP_INITIALIZER, and you can achieve this using two main ways.
With this option, you will simply resolve a factory function, that must resolve to something, otherwise, your application will “hang”.
Let’s assume that you want to check if the user is using RTL or LTR, and if this is the case, then you wanna send them to the right micro-frontend version of your Angular micro-frontend app.
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
const sendToMicroFrontend = () => {
// Some logic here to check the directionality of the language
window.location.href = 'https://bingeoncode.com';
};
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, AppRoutingModule],
providers: [
{
provide: APP_INITIALIZER,
useFactory: sendToMicroFrontend,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppModule {}
We can have yet another example of using the same factory function pattern, to check whether the user has an ad blocker present, in which case, we show nothing on the app but simply an error message.
This can be particularly helpful, as it will save on your server resources.
const checkIfUserHasAdblocker = async () => {
console.log('checkIfUserHasAdblocker');
return new Promise((resolve, reject) => {
/**
* do some logic here for getting adblockerPresent,
* for now, we hard code it to true
*/
const adblockerPresent = true;
if (adblockerPresent) {
reject(new Error('You have adblocker!'));
} else {
resolve('All good!');
}
});
};
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, AppRoutingModule],
providers: [
{
provide: APP_INITIALIZER,
useFactory: () => checkIfUserHasAdblocker,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppModule {}
That is pretty straightforward, but at times you want to get something from maybe the backend, something like configuration data, and you want to make use of the likes of Angular inject. Let us use a different approach for this.
const getConfigFromBackend = (): (() => Observable<any>) => {
const httpClient: HttpClient = inject(HttpClient);
return () =>
new Observable((subscriber) => {
httpClient
.get('https://bingeoncode')
.subscribe((configurations) => subscriber.next(configurations));
});
};
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, AppRoutingModule, HttpClientModule],
providers: [
{
provide: APP_INITIALIZER,
useFactory: getConfigFromBackend,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppModule {}
So, here, we are still using a factory function, however, we are actually using a JavaScript closure, so that we can be able to call inject.
If you wanna learn more about how this is possible in Angular, please check out this piece, you will love it -> How To Use New Angular Inject Outside Constructor Phase
As you can see, Angular APP_INITIALIZER is really powerful, whenever we want to do custom logic, before the application actually intializes.
With that said,