Initialize Angular App with NgRx & APP_INITIALIZER

One of the main challenges I have had was switching my APP_INITIALIZER to use NgRx & effects. APP_INITIALIZER will delay starting up your application until any required data is available for it. For example, if you have an application that depends on user information to start, you may want to delay the start of the application until this data is available.

I have used APP_INITIALIZER before starting to use NgRx and was a little challenging to get it setup with providing the required dependencies like the HTTP services that will pull the user data. Once you get this setup, you do not worry about it anymore. True statement? Maybe! but this was not my case when I wanted to switch to using NgRx and when I got addictive to using NgRx.

In this reading I will take you through what you need to get APP_INITIALIZER work with NgRx store & effects.

Here is how your APP_INITIALIZER will look like without NgRx:

providers: [{
provide: APP_INITIALIZER,
useFactory: initApplication,
multi: true
}]

and your initializing function:

export function initApplication(): Function {
return () => new Promise(resolve => {
resolve(true); // Some http to get data and then resolve
})
}

So how do we add a store here and enable effects to work with the initApplication function and load the app data?

The challenge is that if you injected a store and tried to use effects, they are not ready by the time when your application is initializing. If you dispatch an action to the effect that loads the needed data, it will act like it does not exist. The action will not travel through to your effect, but lets get this working together.

We will start with the regular NgRx steps to enable it in our app. If you are familiar with this, skip to the implementation of the effect below. First create an app state interface. Lets say that our app depends on a list of users to be loaded before it gets initialized. Our interface will look like this:

export interface AppState {
users: Array<any>;
}

Now we will create the actions needed for the cycle of our app loading. You will notice the first tow actions will be used to enable our loader effect and disable it after we are done. Here is our four actions:

Now lets add our reducer to set users to the state when they are available from our effect:

export function appStateReducer(state: AppState, action: AppActions):AppState {
switch(action.type){
case AppActionTypes.UsersLoaded: {
return { ... state, users: action.payload};
}
default: {
return state;
}
}
}

Now add the store module to the app.module.ts

StoreModule.forRoot({appState: appStateReducer})

All the above is very much needed for managing your app state through NgRx, not very specific to our case. It is mostly preparing to the party to start. Our initialization party starts at the effect, but before that, Lets add the store as a dependency in the `initApplication` function. This will enable us to dispatch actions to the loader effect.

Now let’s start working with the loading effect. The effect need to implement `OnRunEffects` Here is how the implementation will look like.

Here is our implementation for this, and you will notice how we used the first two actions we mentioned above to enable and disable the effect through our initialization process.

ngrxOnRunEffects(resolvedEffects$: Observable<EffectNotification>): Observable<EffectNotification> {
return this.actions$.ofType(AppActionTypes.StartAppInitializer)
.pipe(
exhaustMap(()=> resolvedEffects$
.pipe(
takeUntil(this.actions$.ofType(AppActionTypes.FinishAppInitializer) )
)
)
);
}

Now you will notice that our effect gets enabled once we dispatch `StartAppInitializer` action, and we have the `takeUntil` operator to disable this effect once it receives a `FinishAppInitializer` action.

We will also have an effect that will load the users list that is needed before the app initialization. Here is what we have in the same effect:

Now how will the `initApplication` funtion will leverage that?

We will dispatch the action to enable the effect and then dispatch the action to load user information. After that, we will need to subscribe to the user information and only resolve the APP_INITIALIZER promise only when the user information is available in the store. Now here is how the `initApplication` finally look like:

export function initApplication(store: Store<AppState>): Function {
return () => new Promise(resolve => {
store.dispatch(new StartAppInitializer());
store.dispatch(new LoadUsers());
store.select((state: any) => state.appState.users)
.pipe(
filter(users => users !== null && users !== undefined && users.length > 0),
take(1)
).subscribe((users) => {
store.dispatch(new FinishAppInitializer());
resolve(true);
});
})
}

Note how we are waiting for the user information to be loaded in the store then we resolve our promise. Once the promise is resolved, the app load and becomes available for the user. I have notices the rest service I am using gets cancelled if I used it a lot, and as a workaround I used the following to fake the loading delay in replacement of the http call:

return Observable.of([{
userId: 1,
name: "Some Name"
}]).delay(2000);

As I mentioned initializing my app using NgRx have been a little challenging as I have not seen a complete example and it took me a while to figure out this process all together. I hope this was helpful for you and that its all what you need for your example. The full example is posted to my github as a reference in case you wanted to take a look there. I hope you enjoyed your reading.