Developing swift and clutter-free angular apps without any lag is the dream of every Angular developer. But as the apps get scaled up, the amount of API calls also scales up. This results in showing either conventional loading spinners or creating something for the user until the data is fetched. These loading spinners are bad from a UX point of view. We as developers should find solutions to improve the UX of the app. One such solution is the use of Angular Resolver.
So, let’s begin!
To completely understand all the concepts in this article you need to pass this bar –
- Node.js already installed in your system
- Have experience in writing Angular apps
What is an Angular Resolver?
It’s a typescript class that acts as a service in angular terminology. This class implements an interface known as Resolve. The resolve interface provides us a resolve method that can be used to fetch data during the navigation. We can relate the resolve service to a router guard (canActivate, etc) which gets triggered when the app tries to navigate to a specific route.
Why do we need an Angular Resolver?
Let’s go over this by considering two scenarios.
- An angular app with multiple components.
- One of these components has an API call embedded in its ngOnInIt hook via a service.
- The component HTML template has a ngIf* structural directive which is waiting on that API response.
- Once the user navigates to that component, the API gets triggered through the hook and we show the user a simple spinner that tells the user that his/her data is being loaded.
- This scenario is not a bad practice, many apps are written in such a way. But with this approach, we might get into trouble when we want to manipulate that API response with some data which gets available only after the component is ready. We can update the condition in the ngIf* directive to overcome this problem but that will only result in an increase in the complexity of the logic.
- Ideal Solution: Use resolve on the navigation to that component and handle the API call before even loading the component. This way if the API call fails the user won’t be redirected towards the component and if the API call is successful, then the response data will be available to the component beforehand. Since data is available to the component even before it’s loaded we can perform any type of mutation on the data and use it. There won’t be any need for the ngIf* directive on the component template.
- An Angular app has a dashboard for displaying various pictorial graphs that represents data in various forms.
- The dashboard graphs are divided into various components.
- These components have various API calls via services to populate those graphs.
- While fetching those data, the user will be shown a loading spinner instead of these graphs.
- Since all graphical components are children of the dashboard component, when the user first logs in to the dashboard, he/she will see multiple spinners everywhere.
- This will result in negative UX.
- Ideal Solution: Use resolve on navigating or logging in the dashboard, to fetch all the data needed by all the graphical components of the dashboard. Communicate the data from parent to child components and populate pictorial graphs. Once a user logs in, no multiple spinners are seen, just the graphs.
Resolver can greatly increase the UX index of an angular app. It can also help the developers to de-clutter complex logic. It can even make an app faster by reducing the amount of usage of ngIf* structural directive.
Getting Hands Dirty
The following steps are on the assumption that we can easily create an angular project using angular CLI commands. If not, please visit Angular Documentation.
There also will be a link to the DEMO app at the end of this document.
Creating resolve service
- For creating a resolver class we have a couple of different ways.
- First: Manually creating a file: Inside the app directory of the project, just create a file and name it according to Angular semantics i.e. if it’s a component then the file name should be like abc.component.ts, if its a service then its name should be like abc.service.ts and likewise for resolver it should be abc.resolver.ts. After creation, we have to manually create the class structure.
- Second: Using angular CLI commands: Inside the black n white terminal navigate to the angular project and type this –
ng generate or ng g
- To create we will execute this –
ng g resolver test
Use the second approach if you are a beginner. That’s how we create a resolver service in angular projects.
We will fetch the data from APOD APIs in two ways, one will be from the resolver and the other will be directly called out from the component itself. This will elaborate the scenario one of why we need the resolvers section.For how to use and integrate NASA APOD APIs, please visit this GitHub link.
Data Layer –
First, create a data service that will make an HTTP request to APOD API. This service will be like this –
The getAstronomyData() function/method will return an Observable of this custom type NasaApodApiResponseModel.
Creating HTTP API response models is one of the best practices that we can follow while developing Angular applications. This also makes our code type-safe.
Representation Layer –
Now create a component and fetch data from the above service. This component will make a subscription call to the data service in its ngOnInIt lifecycle hook.
Update the component HTML to showcase the picture that we got from the APOD API response.
Notice that we have to use the ngIf structural directive and the loading spinner. Since we are getting the astronomy data when we visit this component, we have to show something to the user, until the API call gets settled.
When we first try to visit this page we will witness this –
Once the response is settled from the API, we will see a beautiful picture of the universe.
Let’s try to remove the loader, it would be nice if we just visit the page see the image itself instead of the loader. To achieve this, we will resolve APOD API response in the resolver.
Initiating API call from resolver –
Notice that we are not subscribing to the service call in the resolve method. This is because we don’t want to have a response from the API in our resolver. We want to execute the API call and return the response to the component as resolved data. The resolved data will be ready even before the component is loaded. Therefore we won’t be needing any ngIf and loading spinners in our component HTML. Also, we can implement error handling in case the API fails, like navigating away to a new route or show an error popup to the user.
Just to enable this resolver we have to add this to a component route. So, let’s modify some routes in our application.
Just like any other, router guard we will add the resolve property to the route and define a key whose value will be returned/resolved by the resolver. Here, I have named the key as astronomyData, it’s up to us to name it.
Accessing the resolved data from the resolver in a component –
We can access the resolved response via injecting the activatedRoute service in our component. The activatedRoute service provides us a data observable. Subscribing this observable gives us access to the response from the APOD API which was resolved just before navigating to this component. We can also access the snapshot property of activatedRoute and access the data from it.
This means whenever we try to visit the ‘planetary/astronomy-resolved’ route in the app, we will have the response ready at the construction of the called component. If for some reason the API fails and returns 404, the user will not be navigated to that component.
This way we can simply mutate the data from the API response even before navigating to a component. Also, there is no need to add ngIf structural directive and loading spinners to indicate the loading of data.
The HTML part of the component will be like this –
We will see this, when we try to navigate to the component, a beautiful picture –
Here you can find the DEMO app.
Washing up our dirty hands now. Even before the birth of a component, we are resolving the data-related worries of our components by using these resolvers in the app. Let’s again go through the pros and cons.
- Advantages –
- Avoid annoying ngIf checks on HTML level, just to safely load data.
- Better user experience with less or no use of loading spinners.
- Clutter-free logic, components can focus on the data they received instead of on how to get the data.
- Simpler error handling, if API fails, redirect the user to a new route, without loading the component.
- Mutation of data, or attaching more logic before accessing the loaded component.
- Disadvantages –
- If the API response, is huge, then there will be a huge delay in previewing the component.
- There will always be a microsecond delay in viewing the preview component, after the triggering route click.
That’s all for now folks. I hope you have learned something from this blog. If you liked it then please hit the thumbs up and share it with your friends, family, or colleagues. Also, please help me improve by giving healthy feedback (below comments). Follow me to get updates on more interesting blogs.
For more angular related topics visit – https://blog.knoldus.com/category/tech-blogs/Angular/