A Beginner's Guide to Reactive Programming in Angular 18 - Part 1

Prerequisites: Node.js version 18.19.1 or higher

App Setup

  • Open a Terminal

  • Install Angular CLI - npm install -g @angular/cli

  • Create a new project - ng new reactive-angular-app

  • Run the project locally - cd reactive-angular-app and npm start

  • The app would be running locally now at http://localhost:4200

Creating Routes, Components, Service and Interface

Navigate to the app folder under the src folder. You will find a file named app.routes.ts, which will have empty routes in it.

export const routes: Routes = [];

We will add routes for Product Page and Cart Page as below. Also we will add a redirect route to product page when the application loads. Wild card routes we have not added for sake of simplicity.

export const routes: Routes = [
  { path: '', redirectTo:'product', pathMatch: 'full' },
  { path: 'product', title: 'Product Page', component: ProductComponent },
  { path: 'cart', title: 'Cart Page', component: CartComponent },
];

Next, we will create the Product Component, Cart Component, Product Service, and Product Interface using the code below.

ng generate component product
ng generate component cart
ng generate service service/product
ng generate interface model/product

Adding Logic to the Component and Service

Inside the product.service.ts file, we will write the logic to get products. We will use the dummyjson API for our examples. For demonstration purposes, we will fetch only selected items.

// model/product.ts

export interface Product {
  id: number;
  title: string;
  price: number;
  discountPercentage: number;
  rating: number;
  images: string[];
}
export interface ProductResponse {
  products: Product[];
  total: number;
  skip: number;
  limit: number;
}
// product.service.ts

import { Injectable } from '@angular/core';
import { Observable, map, catchError, throwError } from 'rxjs';
import { Product, ProductResponse } from "../model/product";

@Injectable({
  providedIn: 'root'
})
export class ProductService {
  private readonly productsUrl = 'https://dummyjson.com/products?limit=10&select=id,title,price,images,discountPercentage,rating';

  constructor(private http: HttpClient) { }

    getProducts(): Observable<Product[]> {
        return this.http.get<ProductResponse>(`this.productsUrl`,)
            .pipe(
                map((response: ProductResponse) => response.products),
                catchError((err: any) => {
                    return throwError(() => new Error('Could not load products'));
                })
            );
    }
}
// product.component.ts

import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { Product } from '../model/shopping';
import { AsyncPipe, CommonModule } from '@angular/common';
import { ProductService } from '../service/product.service';

@Component({
  selector: 'app-product',
  standalone: true,
  imports: [AsyncPipe, CommonModule,],
  templateUrl: './product.component.html',
  styleUrl: './product.component.css'
})

export class ProductComponent {
  constructor(private productService: ProductService) {}
  products$: Observable<Product[]> = this.productService.getProducts();
}
//product.component.html

<div class="bg-white">
  <div
    class="container mx-auto max-w-2xl px-4 py-16 sm:px-6 sm:py-24 lg:max-w-7xl lg:px-8"
  >
    <h2 class="text-2xl font-bold tracking-tight text-gray-900">
      Products Page
    </h2>

    <ng-container *ngIf="products$ | async as products; else loading">
      <div
        class="mt-6 grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-3 lg:grid-cols-4 xl:gap-x-8"
      >
        <div
          *ngFor="let product of products"
          class="group relative bg-white rounded-lg overflow-hidden border border-slate-200 flex flex-col"
        >
          <div class="overflow-hidden bg-gray-200 group-hover:opacity-75 h-44">
            <img
              [src]="product.images[0]"
              alt="{{ product.title }}"
              class="h-full w-full"
            />
            <div
              *ngIf="product.discountPercentage"
              class="absolute top-0 right-0"
            >
              <p class="bg-red-500 text-white text-xs px-2 py-1">
                Discount: {{ product.discountPercentage }}%
              </p>
            </div>
          </div>
          <div class="flex-grow flex flex-col">
            <div class="mt-auto p-4">
              <h3 class="text-sm text-gray-700">

                  <span aria-hidden="true"></span>
                  {{ product.title }}

              </h3>
              <p class="text-lg font-medium text-gray-900 mt-2">
                ${{ product.price }}
              </p>
              <p class="text-blue-400 mt-2">Rating: {{ product.rating }}/5</p>
            </div>
            <button
              (click)="addToCart(product)"
              class="bg-slate-100 text-black w-full px-4 py-2 rounded hover:bg-slate-300 mt-4"
            >
              Add to Cart
            </button>
          </div>
        </div>
      </div>
    </ng-container>
  </div>
</div>

<ng-template #loading>
  <div class="flex justify-center items-center h-screen">
    <p>Loading products...</p>
  </div>
</ng-template>

To be continued in second part.

0
Subscribe to my newsletter

Read articles from mahesh samagandi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

mahesh samagandi
mahesh samagandi