In this article, I will implement an angular router resolver with an explanation. So we will create two component product list and production description. Product List will have a product list resolver and the other will be a product description resolver which takes the product slug from the current route param.

So what is an angular resolver?

Angular resolver is an injectable service class that gives pre-data before component init. Resolver used for pre-fetching data after angular route end.

So how is an angular resolver implemented?

An angular resolver implements by Implementing an angular Resolve interface. and angular Resolve takes two parameters, ActivatedRouteSnapshot, and other RouterStateSnapshot

export declare interface Resolve {
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | T;
}

Note: In this tutorial, we will make some HTTP calls. So we will use storerestapi prototype, Store Rest Api provide fake store data

Let’s start the angular resolver tutorial

Step 01. Generate an angular project and set up

A new angular project is generated with SCSS style and with @angular/router, You can generate other style options like CSS or LESS. Actually, I always use SCSS for styling.

ng new angular-router-resolver --style=scss --routing

Go to project directory angular-router-resolver

cd angular-router-resolver

Edit the app.component.html file to add router-outlet for multiple routes

This Is Angular Router Resolver

Step 02. Generate some necessary router component

Generate ProductList and ProductDescription Component.

ng g c product/productList
ng g c product/productDescription

add product list and product description to our app routing, In th app-routing.module.ts file

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProductDescriptionComponent } from './product/product-description/product-description.component';
import { ProductListComponent } from './product/product-list/product-list.component';

const routes: Routes = [
  {
    path: 'products',
    pathMatch: 'full',
    component: ProductListComponent
  },
  {
    path: 'products/:slug',
    component: ProductDescriptionComponent
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Step 03. Generate Product Service and implement product list and product description HTTP methods

Generate Product Service inside the product directory

ng g s product/product --skip-tests

edit the product.service.ts file, and implement getProducts and getProduct methods

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ProductService {

  baseUrl: string = 'https://api.storerestapi.com/v1'

  constructor(private http: HttpClient) { }

  getProducts(): Observable {
    return this.http.get(this.baseUrl + '/products')
  }

  getProduct(slug: string): Observable {
    return this.http.get(this.baseUrl + '/products/' + slug)
  }
}

Add HttpClientModule for use of HttpClient class. In the app.module.ts file

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
...

@NgModule({
  ...
  imports: [
    ...
    HttpClientModule
  ],
  ...
})
export class AppModule { }

Step 04. Implement product-list resolver

Generate product-list resolver inside product/resolver directory

ng g resolver product/resolver/product-list --skip-tests

Implement ProductListResolver class to return product list observable

import { Injectable } from '@angular/core';
import {
  Router, Resolve,
  RouterStateSnapshot,
  ActivatedRouteSnapshot
} from '@angular/router';
import { map, Observable, of } from 'rxjs';
import { ProductService } from '../product.service';

@Injectable({
  providedIn: 'root'
})
export class ProductListResolver implements Resolve {

  constructor(private productService: ProductService) { }

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable {
    return this.productService.getProducts().pipe(map(res => res?.data));
  }
}

Add Product-list resolver to ProductList route

In the app-routing.component.ts file.

...
import { ProductListResolver } from './product/resolver/product-list.resolver';

const routes: Routes = [
  {
    path: 'products',
    component: ProductListComponent,
    pathMatch: 'full',
    resolve: {
      products: ProductListResolver
    }
  },
  ...
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Step 05. Access Product List data from ProductList Component

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-product-list',
  templateUrl: './product-list.component.html',
  styleUrls: ['./product-list.component.scss']
})
export class ProductListComponent implements OnInit {

  data: any;

  constructor(private route: ActivatedRoute) { }

  ngOnInit(): void {
    console.log(this.route.snapshot.data?.products)
    this.data = this.route.snapshot.data;
  }
}

Render ProductList data to HTML, Edit product/product-list/product-list.component.html file

Product List

Step 06. Implement product resolver

In the product resolver, we will take the product slug from the route param

So Generate a product solver inside product/resolver directory

ng g resolver product/resolver/product

Implement Product Resolver

import { Injectable } from '@angular/core';
import {
  Router, Resolve,
  RouterStateSnapshot,
  ActivatedRouteSnapshot
} from '@angular/router';
import { map, Observable, of } from 'rxjs';
import { ProductService } from '../product.service';

@Injectable({
  providedIn: 'root'
})
export class ProductResolver implements Resolve {
  constructor(private productService: ProductService) { }

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable {
    return this.productService.getProduct(route.params['slug']).pipe(map(res => res?.data));
  }
}

Add Product Resolve to the product description route

...
import { ProductResolver } from './product/resolver/product.resolver';

const routes: Routes = [
  ...
  {
    path: 'products/:slug',
    component: ProductDescriptionComponent,
    resolve: {
      product: ProductResolver
    }
  }
];

...
export class AppRoutingModule { }

Access Product data from the product description component, In the product-description.component.ts file

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-product-description',
  templateUrl: './product-description.component.html',
  styleUrls: ['./product-description.component.scss']
})
export class ProductDescriptionComponent implements OnInit {
  product: any;
  constructor(private route: ActivatedRoute) { }

  ngOnInit(): void {
    this.product = this.route.snapshot.data['product']
  }
}

Render product in product description HTML file

Product Description

Title: {{product?.title}}

Price: {{product?.price}}

Handling Resolver Api Error

You can handle error via RxJs catchError operator,

import { Injectable } from '@angular/core';
import {
  Router, Resolve,
  RouterStateSnapshot,
  ActivatedRouteSnapshot
} from '@angular/router';
import { catchError, map, Observable, of } from 'rxjs';
import { ProductService } from '../product.service';

@Injectable({
  providedIn: 'root'
})
export class ProductResolver implements Resolve {
  constructor(private productService: ProductService) { }

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable {
    return this.productService.getProduct(route.params['slug']).pipe(
      map(res => res?.data),
      catchError(() => {
        return of("Product is not found!")
      })
    );
  }
}

In the case of an error, I will show an alert error message and route it to the product list, and for resolver return EMPTY

import { Injectable } from '@angular/core';
import {
  Router, Resolve,
  RouterStateSnapshot,
  ActivatedRouteSnapshot
} from '@angular/router';
import { catchError, EMPTY, map, Observable, of } from 'rxjs';
import { ProductService } from '../product.service';

@Injectable({
  providedIn: 'root'
})
export class ProductResolver implements Resolve {
  constructor(private productService: ProductService, private router: Router) { }

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable {
    return this.productService.getProduct(route.params['slug']).pipe(
      map(res => res?.data),
      catchError(() => {
        // return of("Product is not found!")
        alert("Product Not Found")
        this.router.navigate(['/products'])
        return EMPTY
      })

    );
  }
}

Implement Resolver base Loader

import { Component, OnInit } from '@angular/core';
import { ResolveEnd, ResolveStart, Router } from '@angular/router';
import { filter, mapTo, merge, Observable } from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  title="angular-router-resolver";

  // Custom Loader Trigger
  isLoading$!: Observable;
  constructor(private router: Router) { }

  ngOnInit() {
    // Custom Loader Trigger
    const loaderStart$ = this.router.events.pipe(
      filter((event) => event instanceof ResolveStart),
      mapTo(true)
    )
    const loaderEnd$ = this.router.events.pipe(
      filter((event) => event instanceof ResolveEnd),
      mapTo(false)
    )
    this.isLoading$ = merge(loaderStart$, loaderEnd$)
  }
}

Add loader in HTML


  
      
          
          
              
          
      
  



Credit goes to the respective owner!

Leave a Reply

Your email address will not be published. Required fields are marked *