Monday 4 July 2022

Angular - Resolver

 https://www.bacancytechnology.com/blog/angular-resolver#:~:text=What%20is%20Angular%20Resolver%3F,navigates%20to%20a%20particular%20component.


What is Angular Resolver?

Angular Resolver is used for pre-fetching some of the data when the user is navigating from one route to another. It can be defined as a smooth approach for enhancing user experience by loading data before the user navigates to a particular component.


Resolver is also a type of service

// Usage: create resolver.service.ts file 

// Extended in module.ts under routes.

// const routes : Routes = [

{ path  : 'test',

   loadChildren() => import ('mymodule.ts')

   resolve : {

  my-resovler : <imported_resolver_name>

}

}

]

// get data in component from resolver using activeRoute

ngOnInit(): void { console.log( 'Activated route data in Component:::', this.activatedRoute.data ); this.activatedRoute.data.subscribe((response: any) =&amp;gt; { console.log('PRODUCT FETCHING', response); this.products = response.my-resolver; console.log('PRODUCT FETCHED'); }); } }

1) Create a new Angular app

Create a new Angular application by executing the below command:

Copy Text
ng new AngularResolver 

2) Create components

Create a components folder having three components – Home, Products, About.

Copy Text
ng g c components/home
ng g c components/about
ng g c components/products

3) Fetching data

Create a service with a file named product.service.ts to fetch the product’s data from the remote source. In this service, create a function called getProducts() that returns an observable containing data from an API.

// product.service.ts

Copy Text
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Injectable } from '@angular/core';
 
import { Product } from '../../interfaces/product';
 
@Injectable({
  providedIn: 'root'
})
export class ProductService {
  url = 'https://fakestoreapi.com/products?limit=6';
  constructor(public http: HttpClient) {}
 
  getProducts(): Observable&lt;Product[]&gt; {
    return this.http.get&lt;Product[]&gt;(this.url);
  }
}

4) Product Interface

Create the Product interface having the following structure.

// product.ts

Copy Text
export interface Product {
  id: string;
  image: string;
  title: string;
  price: string;
  description: string;
}

5) Implement the Resolve method

Create a products-resolver.service.ts file and implement the resolve method from the Resolve interface of the router for fetching server data as follows.

// products-resolver.service.ts

Copy Text
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
 
import { ProductService } from '../product/product.service';
 
@Injectable({
  providedIn: 'root'
})
export class ProductsResolverService implements Resolve&lt;any&gt; {
  constructor(private product: ProductService) {}
  resolve(route: ActivatedRouteSnapshot): Observable&lt;any&gt; {
    console.log('Called Get Product in resolver...', route);
    return this.product.getProducts().pipe(
      catchError(error =&gt; {
        return of('No data');
      })
    );
  }
}

The ProductsResolverService class will automatically subscribe to the getProducts observable and provide the router with the fetched data. Only resolved data could be returned from the method.

6) Route Configuration

Once we are done with the above steps, we need to configure our routes to specify the data needed to be prefetched for each component. For that, modify the routes in the app-routing.module.ts file.

// app-routing.module.ts

Copy Text
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
 
import { AppComponent } from './app.component';
import { AboutComponent } from './components/about/about.component';
import { HomeComponent } from './components/home/home.component';
import { ProductsComponent } from './components/products/products.component';
import { ProductsResolverService } from './services/productsResolver/products-resolver.service';
 
const routes: Routes = [
  { path: 'home', component: HomeComponent },
  {
    path: 'products',
    component: ProductsComponent,
    resolve: { products: ProductsResolverService }
  },
  { path: 'about', component: AboutComponent }
];
 
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

Within the routes, we provide a key “resolve” containing an object to be used for extracting the data, i.e.,

Copy Text
 resolve: { products: ProductsResolverService }

The “resolve” key is assigned with an object having “products” as a key and “ProductsResolverService” as its value, as you can see above. The data is passed to an object with a property named products.

The resolve() function of “ProductsResolverService” is invoked, and the returned Observable is resolved before the component is loaded. The data extracted from the API is assigned to the “products” object and can be accessed in the component.

7) Access the Resolved Data

For accessing the resolved data, we will use the data property of ActivatedRoute service. In the products.component.ts file, use the following code to access the data:

// products.component.ts

Copy Text
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
 
import { Product } from '../../interfaces/product';
 
@Component({
  selector: 'app-products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {
  products: Product[];
 
  constructor(private activatedRoute: ActivatedRoute) {}
 
  ngOnInit(): void {
    console.log(
      'Activated route data in Component:::',
      this.activatedRoute.data
    );
    this.activatedRoute.data.subscribe((response: any) =&gt; {
      console.log('PRODUCT FETCHING', response);
      this.products = response.products;
      console.log('PRODUCT FETCHED');
    });
  }
}

8) Display the data

To display the data, use the following code in the products.component.html file.

// products.component.html

Copy Text
&lt;div class="wrapper"&gt;
  &lt;div *ngFor="let product of products" class="card"&gt;
    &lt;img [src]="product.image"/&gt;
    &lt;div&gt;
      &lt;h3&gt;{{product.title}}&lt;/h3&gt;
      &lt;p class="price"&gt;Price = ${{product.price}}&lt;/p&gt;
      &lt;p&gt;{{product.description}}&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="cart-btn-div"&gt;
      &lt;button type ="button"&gt;Add to Cart&lt;/button&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

9) Add a Loading Spinner

For a better experience, integrate the loading spinner, add the following code in the app.component.ts and app.component.html files.

// app.component.html

Copy Text
&lt;style&gt;
  .content {
    display: flex;
    margin: 82px auto 32px;
    flex-direction: column;
    align-items: center;
  }
  .card-container {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    margin-top: 16px;
  }
  .card {
    border: 1px solid #eee;
    background-color: #fafafa;
    height: 40px;
    width: 200px;
    margin: 0 8px 16px;
    padding: 16px;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  a,
  a:visited,
  a:hover {
    color: #1976d2;
    text-decoration: none;
  }
  a:hover {
    color: #125699;
  }
  }
&lt;/style&gt;
&lt;div *ngIf="loading" id="myModal" class="modal"&gt;
  &lt;div class="lds-roller"&gt;
    &lt;div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;/div&gt;
    &lt;div&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class="content" role="main"&gt;
  &lt;div class="card-container"&gt;
    &lt;a class="card" routerLink="/home"&gt;
      &lt;span&gt;Home&lt;/span&gt;
    &lt;/a&gt;
    &lt;a class="card" routerLink="/products"&gt;
      &lt;span&gt;Products&lt;/span&gt;
    &lt;/a&gt;
    &lt;a class="card" routerLink="/about"&gt;
      &lt;span&gt;About&lt;/span&gt;
    &lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;router-outlet&gt;&lt;/router-outlet&gt;

// app.component.ts

Copy Text
import { Component } from '@angular/core';
import {
  NavigationCancel,
  NavigationEnd,
  NavigationError,
  NavigationStart,
  Router
} from '@angular/router';
 
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  loading = false;
  title = 'angu-res';
  constructor(public router: Router) {
    this.router.events.subscribe(ev =&gt; {
      if (ev instanceof NavigationStart) {
        this.loading = true;
      }
      if (
        ev instanceof NavigationEnd ||
        ev instanceof NavigationCancel ||
        ev instanceof NavigationError
      ) {
        this.loading = false;
      }
    });
  }
}


No comments:

Post a Comment