Angular Resolvers

Until now, we have been using the ngOnInit lifecycle method to subscribe to our service. This method has a disadvantage — If the user has a slow internet connection, the components get loaded before the data is even available to load. This means the users will be stuck with empty components until the data get loaded.

To prevent this from happening, Angular has provided us with resolvers. Lets start by creating a new file called comics.resolvers.ts inside a new folder named resolvers . The router will use this resolver file to retrieve data. Write a new class called ComicsResolver and an @Injectable decorator with providedIn set to root . Also add a constructor and insert a private service called ComicService . Then add a new resolve method that will return the getComics service.

import { Injectable } from '@angular/core';

import { Resolve } from '@angular/router';

import { Comic } from '../models/comic';

import { ComicService } from '../services/comic.service'; @Injectable ({

providedIn: 'root',

})

export class ComicsResolver implements Resolve<Comic[]> {

constructor(private service: ComicService) {}

resolve() {

return this.service.getComics();

}

}

We still need to tell Angular to use this resolver by going to comics-routing.module.ts and adding a resolve property to the routes of ComicListComponent . This property will have a comics key that is set to ComicsResolver .

{

path: '',

component: ComicListComponent,

resolve: { comics: {ComicsResolver}}

}

Here, we have updated the route for ComicListComponent . So we also need to update the actual file of this component. So go to comic-list.component.ts and change the constructor so that it now only injects the private ActivatedRoute .

constructor(private route: ActivatedRoute) {}

In the ngOnInit method, call the this.route.data piped to the map operator that will take the comics property from the data object. Also remove the call to the service and reuse the same subscription that is already there.

ngOnInit() {

this.route.data

.pipe(

map(data => data['comics'])

)

.subscribe(res => this.comics = res);

}

We also need to repeat this for the comic-details component. So create a new resolver file called comic.resolver.ts file inside the resolver folder. Inside, write the same code that we wrote in comics.resolvers.ts , but with a few tweaks as shown below:

import { Injectable } from '@angular/core';

import { ActivatedRouteSnapshot, Resolve } from '@angular/router';

import { Comic } from '../models/comic';

import { ComicService } from '../services/comic.service'; @Injectable({

providedIn: 'root',

})

export class ComicResolver implements Resolve<Comic> {

constructor(private service: ComicService) {}

resolve(route: ActivatedRouteSnapshot) {

return this.service.getComic(route.paramMap.get('id'));

}

}

In the comics-routing.module.ts file, add the resolve key to the comic detail routes, with the comic key set to ComicResolver as the value.

{

path: ':id',

component: ComicDetailComponent,

resolve: { comic: ComicResolver }

}

Remove the comic service from the constructor of comic-detail.component.ts file and call this.route.data inside the ngOnInit method. Also invoke the pipe method and map operator to get the comic key from the data object. Leave the subscribe method as it is.

constructor(

private route: ActivatedRoute,

) {}

ngOnInit() {

this.route.data

.pipe(

map(data => data['comic'])

)

.subscribe(res => this.comic = res);

}

Now your app will load with the data itself, slow internet or not!