Hey everyone, welcome. My name is Dmytro Mrzansky and in this video you will learn how to hook into the initialization process of your Angular application and how to execute some custom logic there. So if you're interested, stay tuned and let's get started. Before we start coding, let's quickly think in which scenarios it might be useful to execute some custom logic during the app initialization, which happens way before the very first root component is being rendered. Well, the one scenario might be when your application has some configuration that comes from the server, and you want to resolve this data before you start to render the component tree, because this config might contain some necessary information like server endpoints that might be eventually used by the rest of the components to fetch the data or some another very important information that we want to know before we start to render the component tree then your application might have maybe some translations for different languages and you want to resolve those translations before you start to render your ui because otherwise you might get this blinking effect when user you first sees some translation placeholder or translation keys and then it is kind of rendered with the real translations which is distracting and it is quite bad user experience and the last use case that comes to my mind is maybe some license checking maybe in order to use your software user has to buy uh the license and before you bootstrap the application you want to check if the license provided by user is valid or if it's not expired and things like that I hope you got the idea why would we need to hook into the initialization process sometimes now when we know the answer for the question why let's try to find the answer for the question how so how we can get angular execute some custom logic when the application is being bootstrapped actually it is quite easy to do we just have to provide some special injection token into the application root injector and namely i'm talking about app initializer injection token which must be a multi-pro provider so we have to set property multi to true and Here we have to provide some function that will be eventually executed by Angular during the bootstrapping.
So let's provide one and for that you can use one of these two dependency providers. What the difference between those two you will see a little bit later in this video. But if you would like to learn more about the rest of them, I have a dedicated video so you can check it out later. but let's continue and for this use case i choose use value dependency provider and as a value for it there will be a function that eventually will be executed by angular during the initialization process and currently let's just console log some string like that and now we can save it and check it in the browser so you can see here the string app initialization which proves that this function has been taken by Angular and executed. But when exactly it happens?
I mean, we know that it is happening somewhere at the very beginning, but still, when exactly? Good question. And actually, it happens when we call method bootstrap module. And in order to understand what is happening under the hood, we have to investigate the source code a little bit. And here you can see the source code of Angular, and this is our Bootstrap module.
This method calls another method called BootstrapModuleFactory, and here inside you can find such interesting string like runInitializers. This is exactly the point when Angular runs initializers that we provided. This method... Run Initializers comes from application init status service that is being injected when we bootstrap the application.
And if we have a look inside, you can see... You see that this service injects App Initializer token, exactly what we provided right here, right? So those initializers are being stored in AppInit's property and when we work, better to say, Angular, when Angular runs initializers, it does very simple stuff. It just goes through the array of providers. and just call them and that's it.
So it happens basically even before the module is being bootstrapped. Now let's get back to the source code of our application already and let's talk about what we can return from this initializer function. And can we return anything at all? Well, let's try to return some maybe a string. yeah and let's see what will happen um as you can see happens nothing so our return value has been ignored but let's try to return something like observable and i will return let's say interval that will be emitting some value every second and let's check what will happen and it looks like we broke the application what just happened.
The thing is that when you return from initializer function either promise or observable in this case angular will be waiting until either promise is resolved or when observable is completed. Only then it will proceed with bootstrapping model and rendering components and so on and so forth. This is very important to understand because now we return kind of infinite observable so we basically blocking bootstrapping kind of forever and in order to unblock our application we should make this observable finite there are multiple operators that might do it for you and so i will be using for instance take operator and say that after one emission from this uh observable it should be completed right so now i can save it and you can see that my application is back again if i for instance say that the value should be emitted every 10 seconds then you will see that my application is being blocked 10 seconds and then after 10 seconds it will get back again and will be rendered as you can see it right now so this is very important thing you have to keep in mind when you write your application initial initializer function it happens because angular bootstraps the application model when done promise is resolved What is done promise? Done promise comes from application init status and there happens the following thing.
When Angular goes through the array of init functions we provided and call this function, the result this function returns is being stored in init result constant. Then Angular checks if this init result constant is promise, then this promise will be pushed into a sync init promises array. Otherwise, if it is an observable, then Angular will convert this observable to the promise. And this new promise will be also added into this async init promises array.
And then Angular creates another promise using promise all method and provides the array of collected promises on this step as an argument. And promise all method works like a following. When each provided promise inside this array is resolved, then the whole promise will be successfully resolved.
If one of these promises is rejected, then the whole promise will be rejected. And if all promises are successfully resolved, then complete function will be called. This function calls the resolve method, and this resolve method eventually resolves the done promise. And then this whole block of the code will be executed so this is the logic and it explains why our application hangs when we provide some infinite observable for instance all right it was so to say theoretical part now let's get back to the source code of our application and let's build something meaningful something that makes sense in real life and i would suggest you to build the following feature so i want to have some initializer module and when i import this module into my application it should provide some initialization function that during application bootstrap will go to the server and fetch some config for me.
This config will contain some server endpoints or some API endpoints that will be used by the rest of my components in order to pull some data. And of course, it should be done using reactive approach, push-based architecture and all other fancy stuff. So the task I hope is pretty much clear so let's start with implementation first of all let's generate a new model as i said i'm going to create some initializer model and using this flag i instruct angular cli to automatically add this new model into the imports array for my application so i generate one here we go and now i can go inside and do some uh clean up we don't need imports and declarations arrays we will be only providing injection tokens and here i will create providers array and there i'm going to provide app initial lizer injection token it is multi-provider and then i will be using but this time i use a factory dependency provider this factory should be a function and and if you remember the value for app initializer must be also the function so we have to return in this case another function which will be called by angular during the initialization and here we should already fire the http call to the server and this task will be handled by separate angular service that we have to generate so I'm going to open my terminal again and generate a new service I'm going to create it under the initializer folder next to my initializer model and I'm going to name this service as a config right and let's keep unit test creation here we go so my config service has been created let's open it and start to implement the logic here first of all we know that this service requires http client in order to do http calls so let's go to app model and import http client model right here and of course we have to import it like that and now we will be able to inject http client right here cool then we would need to create a method that will fire the http call so here below i will create like a fetch endpoints method and already here we're going to execute the following request to the server and call the following endpoint this url will return the list of different endpoints for api for a license check and so on. We are interested only in this URL. So this value will be returned by this request.
And because we're going to implement push-based architecture, I'm going to immediately subscribe to this observable in order to execute this HTTP call. And then the value that this observable returns will be pushed to some public observable that will be exposed by this service and if some component or another service is interested in this data it just subscribed to this public observable and receives data from there but before we create it we would like to create another property that will be kind of storing our endpoints and will be pushing new values into this public observable. For that, I will create a private property called endpoints and it's going to be a new behavior subject because a behavior subject always has some value and by default we will have null means that it is not resolved at all and we have to also describe the data that will be pushed by this behavior subject and the value will be either null or it might be endpoints that are coming from this endpoint you can see it this is the object that has two properties api and license check so let's create some interface right here call it endpoints like that and it has api which is which is string and it also has license check that is also string so this is what will be pushed by this behavior subject And then we will create a public observable. I will call it API probably.
And this observable I will build from my endpoints and I will convert behavior subject to observable. And already here, I will apply a couple of operators. First of all, it will be a filter because I would like to filter out the null value. I'm interested already in resolved data right so here i will have endpoints and this predicate function will filter out null value for me and because i'm interested only in api key i would like to transform my endpoints a little bit using map operator and here i want to fetch only api key from there so we have our public api and by the way it should be read only so let's do it like that and now we have to push some values from here into this observable how do we do that quite easy actually uh if we successfully fetch the data it will be called next callback and this callback will get all fetched endpoints right this is what our server returns and then we will call next method for our endpoints and provide actually endpoints we got from the server why does it complain okay because i have to describe what the data returns this endpoint so it's going to be endpoints and now you can see that error disappears so such a way we push everything what returned this endpoint we push it via behavior subject to this observable and all subscribers for this observable will get then the result that this endpoint returns also we can handle the error case for this call and you could dispatch some maybe fallback urls for that or handle your error somehow differently but in my case i just provide some fallback urls Something like that and now our service is ready to be used and we can save these changes and go to our initialize initializer model and here we can already inject this service into our Factory provider in order to inject it. I will introduce Deps property and here inside depth array I say that I need dependency for the config service and after that this config service will be available as an argument for a factory function.
So here will be my resolved config service. Now what we are going to do inside this initializer function? Well first of all we would need to fire the http call to our server in order to fetch endpoints right and now we should kind of block the bootstrapping process and wait until this request is resolved and once it is resolved we can resume bootstrapping process and angular can proceed further and in order to do that we will return the api observable that comes from the config service and here We don't want to block our application forever, but we will block it until we get the very first value from this stream like that. So look what will happen.
App initializer will trigger fetch endpoints and immediately subscribes to this API observable. Then after some time, maybe. one second or less. This HTTP call is completed and the value from this HTTP call will be pushed to this API stream. And then once the very first value comes through the stream, we take this value and complete the stream.
And then application can proceed bootstrapping, but the data will be already resolved and will be stored. inside this endpoint property. And now we are ready to go and test our initializer.
So I will save all these things right here. And let's say your app components need some data from the server. So now what you would do is to inject HTTP client like that.
And you also inject your config. which is config service and already then inside i don't know ng on init lifecycle hook you could do the following like you read the api url from the config first of all then you could use some operator like switch map right and here you will get the url result from them config and Here you could use this value to do your HTTP call and call some already concrete end point like that. And don't forget that we have to subscribe to this observable in order to execute it.
You could subscribe to it via a sync operator if you want, it would be even better. But yeah, let's check if our application works and it looks okay. If we switch to the network tab, We see two HTTP calls, the first one endpoints.
This is what we call during the app initialization. It returns endpoints for us. And then this endpoint is being used for the second HTTP call when we call users. This is what we have done right here. So it works fine.
If you don't need reactivity like I did right here, you could implemented slightly differently you could create some getter called api and from this getter you would return the current value from endpoints behavior subject using get value method and from there you would read the api property like that and then inside your app component you would refactor it by removing this part removing switch map and this part as well and URL you would replace with this config and API so like that and it will be working the same way so you will get the same result I choose this reactive approach because usually you see examples using this approach and I wanted to show you how it would look like when you use push-based architecture as well Alright guys, that was it. I hope this video was useful and if so, please share it with your colleagues and friends Don't forget to subscribe to my other social media because there I post different Angular tips as well and Of course check out my video courses if you want to get some cool knowledge about angular I will drop links in the video description as well as coupon codes so you can check them out otherwise, I wish you productive week ahead stay safe and see you in the next video