In this article, I am implementing product details based on the product_slug route param. I will implement a reactive service method that is dependent on route params observable, So what does it mean? That’s mean when the parameter change by another product_slug the service trigger another API call with a changed product_slug
Note: in this guide, I am going to use fake rest API data from https://storerestapi.com product data.
Let’s prepare a fresh project
start a new project and create a product base feature module that will have a product page and product details (that’s details page is a reactive dynamic route )
ng new angular-reactive-service
Generate product module and product service(that’s service will have product details reactive method). Also, generate a product list and product details component.
ng g m product --routing ng g s product/product --skip-tests ng g c product/product-list ng g c product/product-details
Link product list and product details page in product routing module. In the product.routing.module.ts
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ProductDetailsComponent } from './product-details/product-details.component'; import { ProductListComponent } from './product-list/product-list.component'; const routes: Routes = [ { path: '', component: ProductListComponent, }, { path: ':slug', component: ProductDetailsComponent } ]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule] }) export class ProductRoutingModule { }
Link product module in the project, in the app-routing.module.ts file
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; const routes: Routes = [ { path: '', loadChildren: () => import('./product/product.module').then(m => m.ProductModule) } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
Make sure cleanup app.component.html file
<router-outlet></router-outlet>
import HTTP module
In the example, I am going to make some HTTP call. So angular require HttpClientModule
for make HTTP calls. So import and export HttpClientModule in app.module.ts file
import { HttpClientModule } from '@angular/common/http'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, AppRoutingModule, HttpClientModule ], exports: [ HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Let’s implement product service
In the product.service.ts file, I implement two methods getProducts and getProduct. Both methods make HTTP call
getProducts() method: getProducts is to create an HTTP call for a list of products.
getProduct() method: the getProduct method is also an HTTP call for single product details which is based on the product slug. getProduct method is one of our reactive service methods. which is take Obserable of productSlug. And this productSlug comes through the current route param
import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable, switchMap } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class ProductService { constructor(private http: HttpClient) { } getProducts(): Observable<any> { return this.http.get('https://api.storerestapi.com/products') } getProduct(productSlug: Observable<string>): Observable<any> { return productSlug.pipe( switchMap((slug) => { return this.http.get(`https://api.storerestapi.com/products/${slug}`) }) ) } }
Implement product list component
Edit product-list.component.ts file
products$: Product property container list of product list from HTTP call when be component initialize.
import { Component, OnInit } from '@angular/core'; import { map, Observable } from 'rxjs'; import { ProductService } from '../product.service'; @Component({ selector: 'app-product-list', templateUrl: './product-list.component.html', styleUrls: ['./product-list.component.scss'] }) export class ProductListComponent implements OnInit { products$!: Observable<any[]>; constructor(private productService: ProductService) { } ngOnInit(): void { this.products$ = this.productService.getProducts().pipe(map(res => res?.data)) } }
Edit product-list.component.ts file
<ul *ngIf="(products$ | async) as products;else empty"> <li *ngFor="let product of products"> <a [routerLink]="['/' + product?.slug]">{{ product?.title }}</a> </li> </ul> <ng-template #empty> <h3>Loading ...</h3> </ng-template>
Implement product details (reactive page)
In the product details component, I will show the product list which was the product-list component. and I will show route base product details in the same component
Edit product-details.component.ts file
import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { map, Observable } from 'rxjs'; import { ProductService } from '../product.service'; @Component({ selector: 'app-product-details', templateUrl: './product-details.component.html', styleUrls: ['./product-details.component.scss'] }) export class ProductDetailsComponent implements OnInit { product$!: Observable<any> constructor(private route: ActivatedRoute, private productService: ProductService) { } ngOnInit(): void { const productSlug$ = this.route.params.pipe(map((params: any) => params?.["slug"])) this.product$ = this.productService.getProduct(productSlug$).pipe(map(res => res?.data)) } }
Edit product-details-component.html file
<app-product-list> element component render list of product. And in the right, product details will be rendered
<div *ngIf="(product$ | async) as productObj;else empty" style="display: grid;grid-template-columns: repeat(3, minmax(0, 1fr));"> // Product list component <app-product-list></app-product-list> // Product details <div style="grid-column: span 2/span 2;"> <h1>{{productObj?.title}}</h1> <hr> <p><strong>Price:</strong> {{productObj?.price}}</p> <p><strong>Created At:</strong> {{productObj?.createdAt | date:"medium"}}</p> <p><strong>Created By:</strong> {{productObj?.createdBy?.name}}</p> <p><strong>Category:</strong> {{productObj?.category?.name}}</p> </div> </div> <ng-template #empty> <h3>Loading ...</h3> </ng-template>
Checkout code: https://github.com/techincent/angular-reactive-service