Using resolvers in Angular
Let's explain how to use a resolver with a basic Angular application.
What's the matter?
Well, frequently our pages get the content from an API, and the API response takes some time. Therefore, we are forced to use a loading page or spinner while the data is completely resolved.
However, there is another way to handle this behavior.
Using resolvers
Angular has this amazing feature called resolver
which helps us to handle the time between the redirection to a page
and the resolution of the necessary data to render the page.
Angular prevents the load of the page until the data is completely resolved. Thus, our component is always first rendered with all the data.
Let's see it in action.
In this example, I'll be using a main page with a button that redirects to another page with fake content.
First, we need to create a new resolver in our Angular application. Run the following command:
$ ng n resolver article
Then, the new resolver should look like this
1import { Injectable } from '@angular/core'; 2import { 3 Router, Resolve, 4 RouterStateSnapshot, 5 ActivatedRouteSnapshot 6} from '@angular/router'; 7import { Observable, of } from 'rxjs'; 8 9@Injectable({ 10 providedIn: 'root' 11}) 12export class ResolverResolver implements Resolve<boolean> { 13 resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> { 14 return of(true); 15 } 16} 17 18
As you can see, it's just a service class which implements a Resolve
class with the resolve method
.
By default, the resolve is of type Observable
. But, you could return a Promise
too.
In addition, the resolve method has the route
and state
parameters in case you need to use them.
For example, to get a param id or get the current state.
However, we don't need to use them. So, replace the code with:
1import { Injectable } from '@angular/core'; 2import { Resolve } from '@angular/router'; 3import { delay, Observable, of } from 'rxjs'; 4import { article } from './constants/article-mock'; 5import { Article } from './interfaces/article.interface'; 6 7@Injectable({ 8 providedIn: 'root' 9}) 10export class ArticleResolver implements Resolve<Article> { 11 resolve(): Observable<Article> { 12 return of(article).pipe( 13 delay(2000) 14 ); 15 } 16} 17
In this example, it's quite simple. We get rid of all the useless imports.
And, we are returning and Observable
with the of
operator with a mock data set
that contains an object with {title: string; content: string; author: string}
.
But, in a real application, you should return the API request.
Also, we are using the pipe
and the delay
operators to simulate a slow request to the backend
in order to show you how the new page wouldn't be rendered until this resolve is finished.
Now, let's utilize this new resolver.
In the app-routing.module.ts
file, let's add a configuration in our component Route
1import { ArticleResolver } from './article.resolver'; 2 3const routes: Routes = [ 4 { 5 path: 'main', 6 component: MainComponent 7 }, 8 { 9 path: 'fake-page', 10 component: FakePageComponent, 11 resolve: { article: ArticleResolver } 12 }, 13 { 14 path: '**', 15 pathMatch: 'full', 16 redirectTo: '/main' 17 } 18]; 19
In short, we are only adding a resolve
property in the fake-page
configuration, which it's just
with our resolve class in and object with any name, in this case article
Now, if we try to navigate to localhost:4200/fake-page
, this redirection should take 2 seconds. Due to the delay(2000)
.
Next, let's get the data from the fake-page.component.ts
file:
1export class FakePageComponent implements OnInit { 2 3 article?: Article; 4 5 constructor(private activatedRoute: ActivatedRoute) { } 6 7 ngOnInit(): void { 8 const { article } = this.activatedRoute.snapshot.data; 9 this.article = article; 10 } 11} 12
To achieve this, we need to inject ActivatedRoute
because it is in this class where our resolved data lives.
And, in order to get the resolved data, we can destructure it from const { article } = this.activatedRoute.snapshot.data;
because we name it article in the routing configuration.
Finally, we only need to render the content of our page in the fake-page.component.html
file:
1<section class="container"> 2 <h1>{{article?.title}}</h1> 3 <article class="article"> 4 {{article?.content}} 5 </article> 6 <p class="author">- by {{article?.author}}</p> 7</section> 8
And this should be the final behavior.
Summary
We just learned how to implement the resolve Class in a simple Angular application. We learn, that we only prevent the new page from being redirected until the asynchronous data is resolved. You can find the repository of this example in here.
References: