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
1) Create a new Angular app
Create a new Angular application by executing the below command:
2) Create components
Create a components folder having three components – Home, Products, About.
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
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<Product[]> { return this.http.get<Product[]>(this.url); } }
4) Product Interface
Create the Product interface having the following structure.
// product.ts
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
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<any> { constructor(private product: ProductService) {} resolve(route: ActivatedRouteSnapshot): Observable<any> { console.log('Called Get Product in resolver...', route); return this.product.getProducts().pipe( catchError(error => { 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
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.,
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
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) => { 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
<div class="wrapper"> <div *ngFor="let product of products" class="card"> <img [src]="product.image"/> <div> <h3>{{product.title}}</h3> <p class="price">Price = ${{product.price}}</p> <p>{{product.description}}</p> </div> <div class="cart-btn-div"> <button type ="button">Add to Cart</button> </div> </div> </div>
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
<style> .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; } } </style> <div *ngIf="loading" id="myModal" class="modal"> <div class="lds-roller"> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> </div> </div> <div class="content" role="main"> <div class="card-container"> <a class="card" routerLink="/home"> <span>Home</span> </a> <a class="card" routerLink="/products"> <span>Products</span> </a> <a class="card" routerLink="/about"> <span>About</span> </a> </div> </div> <router-outlet></router-outlet>
// app.component.ts
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 => { 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