Building real-world software having multiple calls to the server can be full of errors and bugs. We know that creating Angular applications can be challenging sometimes. This occurs because the server responses are delayed and this takes place after you make an API call that directly affects the user interface.
There are several ways for managing to retrieve and display data from an API, but the most common way to do this is to route a user to a component and get the required data. You can also do this using the Angular route resolver approach which allows you to retrieve data before the user navigates to the new route.
There are various ways to improve user experience, such as following the best practices of Angular development and displaying a progress indicator. Now you must be wondering then what can be achieved with an Angular Resolver so let’s stick to the topic and dig deeper to know more about Angular Resolver.
In this post, we’re going to discuss in detail Angular/Route Resolver, why developers choose Angular resolvers while building an application, and how to implement it. But before we directly jump to the implementation part, let us first know why choose Angular Resolver and then proceed further.
1. Why Choose Angular Resolvers?
Developers choose Angular Resolvers as it allows their application to retrieve remote data from the server before the activatedRoute of the next component is activated. It is not necessary that you must have a spinner until the data is fetched because it is impossible to navigate to the next component unless fetching the server data.
In simple words, we can say that an Angular Route Resolver is used to retrieve critical data before the user navigates from one route to another. It would be also fair to say that it helps developers to enhance the user experience of the application just by loading necessary data and information before the user navigates to a particular component.
Let’s take one example here to understand it better. Suppose we want to display an array of items in a component received in an unordered list. In order to perform this, you must have *ngIf=” some condition” where the business logic is completely based on the array length, which can be modified once the API call is successful.
Still confused? Don’t worry, we suggest you watch the below-mentioned YouTube Video and know how to resolve your data before a certain route will be activated by using a router resolver.
Now, let us see an Angular resolver example for a better understanding and discuss the implementation part.
2. Angular Resolver Example: How to Implement?
Before you set up the project, make sure to follow certain steps as follows:
First, create a new Angular app by executing the following command:
ng new AngularResolver |
Now, create the components folder that will contain About, Home, Items, Categories components.
ng g c components/home ng g c components/about ng g c components/items ng g c components/categories |
After creating the components folder, fetch the item’s data from the remote source.
// item.service.ts
import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { Injectable } from '@angular/core'; import { Item} from '../../interfaces/item; @Injectable({ providedIn: 'root' }) export class ItemService { url = 'https://fakestoreapi.com/items?limit=6'; constructor(public http: HttpClient) {} getItems(): Observable<Items[]> { return this.http.get< Items[]>(this.url); } } |
Once you have the required data, you can now create Item interface having the structure as mentioned below:
export interface Item { item_id: string; item_image: string; item_title: string; item_price: string; item_description: string; } |
Now create the item-resolver.service.ts file and implement the resolve method to fetch server data as follows:
import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; import { Observable, of } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { ItemService } from '../item/item.service'; @Injectable({ providedIn: 'root' }) export class ItemsResolverService implements Resolve<any> { constructor(private item: ItemService) {} resolve(route: ActivatedRouteSnapshot): Observable<any> { console.log('Called Get item in resolver...', route); return this.item.getItems().pipe( catchError(error => { return of('No Items); }) ); } } |
Now, let’s implement a route to use a resolver before navigating data to a route that displays the gathered data.
2.1 Setting Up the Project
For this tutorial, you need to build from a default Angular project generated with @angular/cli.
npx @angular/cli new angular-route-resolvers-example --style=css --routing --skip-tests |
For this, you’re required to configure a new Angular project with styles set to “CSS”, routing enabled, and skipping tests.
Go to the newly assembled project directory:
cd angular-route-resolvers-example |
2.2 Building a Resolver
Now implement a simple resolver that returns a string after a pause of 2 seconds. This is a proof of concept that represents that you can discover the fundamentals of wiring routes that are applied to complex and larger projects.
For this to work smoothly, you must create a separate class for the resolver in a file of its own.
./node_modules/@angular/cli/bin/ng generate resolver news |
To do this, developers can use the @angular/cli to render a resolver named news.
src/app/news.resolver.ts
import { Injectable } from '@angular/core'; import { Resolve } from '@angular/router'; import { Observable, of } from 'rxjs'; import { delay } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class NewsResolver implements Resolve<Observable<string>> { resolve(): Observable<string> { return of('Route!').pipe(delay(5000)); } } |
To implement the router’s Resolve interface make sure that your class has a resolve method. Whatever is retrieved from the resolve method will be the resolved data.
2.3 Configuring Routes
Lastly, you need to configure the routes to specify the data as follows:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { NewsResolver } from './news.resolver'; import { TopComponent } from './top/top.component'; import { HomeComponent } from './home/home.component'; const routes: Routes = [ { path: '', pathMatch: 'full', component: HomeComponent }, { path: 'top', component: TopComponent, resolve: { message: NewsResolver } } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { } |
2.4 Accessing the Resolved Data in the Component
In the component, you’re allowed to access the resolved data using the data property of ActivatedRoute’s snapshot object.
import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @Component({ ... }) export class TopComponent implements OnInit { dataItem: any; constructor(private route: ActivatedRoute) {} ngOnInit(): void { this.dataItem = this.route.snapshot.dataItem; } } |
Now you can also access the Route in the component!
<p>The message: {{ dataItem.message }}</p> |
Now don’t forget to compile your application at this point.
npm start |
Lastly, visit the localhost:4200/top in a web browser.
Output The message: Route! |
2.5 Handling Errors
If you find any error while retrieving or fetching the data, you can deal with it in the resolver using RxJS’s catch operator. It will look like this.
src/app/news/resolver.ts
import { Injectable } from '@angular/core'; import { Resolve } from '@angular/router'; import { Observable, of } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { NewsService } from './news.service'; @Injectable() export class NewsResolver implements Resolve<any> { constructor(private newsService: NewsService) {} resolve(): Observable<any> { return this.newsService.getTopPosts().pipe(catchError(() => { return of('No new feeds available currently. Please come back later.'); })); } } |
Or return an EMPTY observable and return the user to the root path.
src/app/news/resolver.ts
import { Injectable } from '@angular/core'; import { Router, Resolve } from '@angular/router'; import { Observable, EMPTY } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { NewsService } from './news.service'; @Injectable() export class NewsResolver implements Resolve<any> { constructor(private newsrouter: Router, private newsService: NewsService) {} resolve(): Observable<any> { return this.newsService.getTopPosts().pipe(catchError(() => { this.newsrouter.navigate(['/']); return EMPTY; })); } } |
3. Final Thoughts
So that’s it for the post. Now we assume that you can easily implement a route resolver before navigating to a route that displays the gathered data.
We hope you find this post helpful. It would be great for us if you share this post on Facebook or Twitter to reach more audiences. Feel free to ask your queries in the comment section given below and we will get back to you soon. Now what are you waiting for? Go and start building your own Angular application with the best dedicated development teams.
Thank you!
Great Articles! This article explains in a very simple manner how we can manage server calls in angular applications. Article is a simple guide on how we can implement Angular Route Resolver to mitigate these issues.