It always again happens (especially in real world scenarios) that you need to configure your Angular app at runtime. That configuration might be fetched from some backend API, or it might be some simple JSON configuration sitting on your deployment server. The key here is that you want to be able to dynamically modify and adjust that configuration without the need to re-compile and re-deploy your application. Also, you most probably want that configuration to be loaded and ready once your application bootstrapping is done.
In this lesson we learn how to make use of the APP_INITIALIZER
to hook into Angular's initialization process. That will allow us to inject some configuration into our app just when it is about to start up.
Instructor: [00:01] There are a lot of scenarios in real world applications where you actually want to configure your app at runtime based on some conditions. That could be, for instance, just to visualize the version of that application, or you could even want to configure the behavior of the application based on which client has that application where is actually deployed.
[00:20] Now, one possibility is, of course, go in our app component, and download that configuration here. You basically do some HTTP requests, get that configuration, and then inject it here in our application. What you would want to do, however, is to prevent your application from running until that configuration has actually arrived.
[00:39] You could actually use your NGF, and say something like config loaded, which you then basically set in your app component once the configuration's here. That would mean then the components inside here would then start up, and continuing running.
[00:53] There is a better concept, however, in Angular what we can use. That is basically to use the so-called app initializer. We can import that from Angular core here, and then what we can do is to define here a provider, where we say provide the app initializer, and use a factory function, which we'll define here.
[01:12] Say here, const app initializer function. That is actually a function which should return another function, which then basically our app initializer can load. We will use that function here below. We have to specify multi true so that we can actually register multiple app initializers here.
[01:33] Finally, we can even provide some dependencies to our app initializer function here, which in this case, is simply empty. Now, let's write here some console log statement. When our application refreshes, you can see here how our app initializing is being printed to our console.
[01:52] Now, let's make this a bit more real life, and let's here use that assets folder to create some data folder inside there. Let's say we have here our app config, which is a JSON file which we could actually deploy via our deployment pipeline dynamically.
[02:09] Let's enter here some configuration. In this case, I specify the name of our app, and let's say, version number. Obviously, there could be other stuff which is relevant for your application.
[02:20] Next, let's then create a specific service which will be responsible for loading our configuration. Let's call it app config service. The scaffold here will look something like this. We will need here, obviously, the HTTP client, which we can import here from the common HTTP part.
[02:42] Then let's say we have a function, load app config, which actually executes that HTTP call. In our example of our Angular CLI project, we will call the assets folder, the data folder inside there, and then the app config, which we just specified.
[02:58] Normally, in an HTTP call, you would then subscribe to that, and basically take that data. Let's say this.appConfiguration equals data. That will be here, private variable, appConfig, where we store actually the configuration, such that later on, we can then get that config from some other places.
[03:21] Now, what's here important to note is that we cannot actually use here an observable, because in this case, basically, our load app config would return an observable. We have to use a promise. We will see immediately why that's the case.
[03:35] Let's simply transform this to a promise. As a consequence, also here, we have to use the den for R then to subscribe. It's actually pretty easy to configure that.
[03:43] Now, let's go back to our app module, and provider section, we will have to import here our newly created app configuration service. It obviously has to be imported at the very top here. Now, what we actually want is to inject it inside our app initializer function, so we will get this here as a dependency.
[04:04] Here, we can then do appConfig.loadApplicationConfiguration. Finally, we also need to make sure that we get our dependency injected. We do that by providing it to that depth property down here.
[04:17] Now, note we return here the load application config return time, which in our case, is actually here the promise. Whenever then our app initializer sees here a promise being returned, it will wait until that promise is being resolved.
[04:32] We can verify that by jumping to our app component. In our app component, what we are going to do is to get the configuration service. Then the ngOnInit, we can do this.title, for instance, equals this.configService.getConfig, which is now loaded by this time. We inject here basically the name of our application.
[04:54] Now, let's save this. As you can see now, we get the name that has been configured in our app config JSON file.
Hey. Hmm...maybe I should clarify the Observable thing better in the vid. The thing is basically that the initializer function that you return to Angular doesn't support Observables, but just Promises or plain returns. Described it here: https://juristr.com/blog/2018/01/ng-app-runtime-config/
What exactly do you mean with "comparing to the root Resolver"?
I'm not asking "Does it support observables or not?" :) I'm asking "Why the initializer function does not support them?"
root Resolver
for Router you can have a common parent route with Resolver Guard
Is missing an important part, maybe more server related, but the config asset should not be available from outside for security reason.
@Georg: Agree, here it really depends. You could have "public" configuration info, which is not sensitive to the current user's permission, and you may have "private" configuration information, which might be.
So in general, if you expose the configuration from some API, I usually filter the response based on the user's permissions. So if you call the configuration API before login, it might just return the publicly visible config which the app might need to adjust the UI etc...and once the user is logged in, another call to the config could then return further keys, which only matter to this specific user. Really depends on your use case.
In general, everything that's deployed with the frontend client, should not depend on the user's permissions of course, as it is visible from anyone that's able to access the app :)
I missed the part why it cannot be an Observable. and also what is the difference comparing to "root" Resolver?