In the previous article, we have seen how to create modern page loader using Router Events in Angular 6. It’s nice to show a page loader when navigating one page/component to another page/component, but there are two main issues here,
- The Component view is displayed before loading data is needed to display on that component view
- Page loader completes its processing before the component view is loaded
We will see in this article how we can resolve these issues.
While working on the Angular live project, we use a real-world API and there might be some delay before the data to display is returned from the server and in that case, we don’t want to display a blank component to the users when waiting for the data. Router Resolve API helps us a lot to resolve this issue.
Router Resolve API
It's preferable to pre-fetch data from the server so it's ready the moment the route is activated. This also allows you to handle errors before routing to the component.
Check the Angular official documentation here for more details.
In short, if you want to delay rendering the routed component until all necessary data to be displayed on this component is fetched from the server, Resolve API helps us.
Output
Application after applying the Resolve will work like below,
Implementation
To implement the Resolve API in an Angular application, we need to import Resolve interface from @angular/router package, implement Resolve interface and implement the Resolve() method. This method can return a Promise or an Observable.
Let’s understand this powerful API with the help of the below example.
We will build an Angular application where we will call JSONPlaceHolder API to get post data and will show the data in the user-posts component. Here, we will make sure that my user-posts component will not load or display until all user posts data from API is ready to load or display.
Creating an Interface (post.interface.ts)
- export interface IPost {
- userId: string;
- id: number;
- title: string;
- body: string;
- }
Creating a service (post.service.ts)
- import { Injectable } from "@angular/core";
- import { HttpClient } from "@angular/common/http";
- import { Observable } from "rxjs";
- import { delay } from "rxjs/operators";
- import { IPost } from "../post.interface.ts";
-
- @Injectable({
- providedIn: "root"
- })
- export class PostService {
- private url = "http://jsonplaceholder.typicode.com/posts";
-
- constructor(private http: HttpClient) {}
-
- getPosts(): Observable<IPost[]> {
- return this.http.get<IPost[]>(this.url).pipe(delay(1000));
- }
- }
Creating a Resolver (user-posts.resolve.ts)
- import { Injectable } from "@angular/core";
- import { Resolve, ActivatedRouteSnapshot } from "@angular/router";
- import { Observable } from "rxjs";
- import { PostService } from "./../services/post.service";
- import { IPost } from "../post.interface.ts";
-
- @Injectable()
- export class UserPostsResolve implements Resolve<IPost[]> {
- constructor(private postService: PostService) {}
-
- resolve(route: ActivatedRouteSnapshot): Observable<IPost[]> {
- return this.postService.getPosts();
- }
- }
Registering Resolve in Routing module and Routing Configuration (app.module.ts)
Import this resolver in the app.module.ts (wherever your routing module and routing configuration exists. In my case, Routing configuration is defined in app module itself) and add a resolved object to the user-posts.component.ts route configuration.
- imports: [
- BrowserModule,
- HttpClientModule,
- FormsModule,
- RouterModule.forRoot([
- { path: '', component: HomeComponent },
- {
- path: 'userposts',
- component: UserPostsComponent,
- resolve:{
- userposts:UserPostsResolve
- }
- },
- { path: 'userposts/:id', component: UsrProfileComponent },
- { path: 'bloggers', component: BloggerComponent },
- { path: 'contact-us', component: ContactFormComponent },
- { path: '**', component: NotFoundComponent }
- ]),
- BrowserAnimationsModule,
- MaterialModule,
- SlimLoadingBarModule.forRoot()
- ],
- providers: [
- PostService,
- [{ provide: 'BASE_URL', useFactory: getBaseUrl() }],
- ScrollDatService,
- UserPostsResolve
- ],
Creating a component
To create a user-posts component, use the following command.
ng g c UserPosts --module app
user-posts.component.ts
- import { Component, OnInit } from "@angular/core";
- import { ActivatedRoute } from "@angular/router";
-
- import { PostService } from "./../services/post.service";
- import { IPost } from "../post.interface.ts";
-
- @Component({
- selector: "app-user-posts",
- templateUrl: "./user-posts.component.html",
- styleUrls: ["./user-posts.component.css"]
- })
- export class UserPostsComponent implements OnInit {
- userPosts: IPost[] = [];
-
- postObject$;
-
- constructor(
- private postService: PostService,
- private route: ActivatedRoute
- ) {}
-
- ngOnInit() {
- this.userPosts = this.route.snapshot.data.userposts;
- }
- }
user-posts.component.html
- <ul class="list-group">
- <li *ngFor="let post of userPosts" class="list-group-item">
- <button (click)="updatePost(post)" class="btn btn-default btn-sm">Update</button>
- <button (click)="deletePost(post)" class="btn btn-default btn-sm">Delete</button>
- <a [routerLink]="['/userposts',post.id]">{{post.id}}</a> - {{post.title}}
- </li>
- </ul>
As per the above code, when you navigate from home component to user posts component, you will see loading bar as running until component loads data and displays the component. Now, run your application and see how resolve API works.
Check the below post to help out with how page loader is implemented in the Angular application.
Summary
Through this article, we learned how to implement Resolve API in an Angular application.
Write to me in the comment box in case you need any help or you have any questions or concerns. Have a good day!