🧱 Lecture 9B : Product Management (Angular)
Source: Dev.to
Introduction
This module focuses on building complete product management functionality, including full CRUD operations and seamless integration between the frontend interface and backend APIs. It ensures that users can create, update, view, and delete products efficiently while maintaining a smooth and responsive experience across the system.
Administrators will be able to:
- Manage products
- Communicate with the backend APIs you’ve already built
Topics Covered
- Auth Guard
- Full CRUD page for Product
- Product Service
- Product Route
- Update App Routes
- Update Menu
Auth Guard
Path: src/app/services/authguard/auth-guard.service.ts
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { AuthApiService } from '../api/auth.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuardService {
constructor(
private authService: AuthApiService,
private router: Router
) {}
canActivate(): boolean {
const token = this.authService.getToken();
const refreshToken = this.authService.getRefreshToken();
// If no token and no refresh token, redirect to login
if (!token && !refreshToken) {
this.router.navigate(['/auth/login']);
return false;
}
// If no token but refresh token exists, try to refresh
if (!token && refreshToken) {
this.attemptTokenRefresh(refreshToken);
return false; // Wait for refresh attempt
}
// If token exists, user is authenticated
if (token) {
return true;
}
return false;
}
private attemptTokenRefresh(refreshToken: string): void {
this.authService.refresh({ token: '', refreshToken }).subscribe({
next: () => {
// Token refreshed successfully, reload current route
this.router.navigate([this.router.url]);
},
error: () => {
// Refresh failed, redirect to login
this.authService.logout();
this.router.navigate(['/auth/login']);
}
});
}
}
UI Pages using PrimeNG
Create the following three files for the Product feature:
src/app/features/products/product/product.htmlsrc/app/features/products/product/product.scss(blank file)src/app/features/products/product/product.ts
product.html
<div class="card">
<p-toolbar>
<ng-template pTemplate="left">
<button pButton label="Manage Products" class="p-button-success"></button>
</ng-template>
</p-toolbar>
<p-table #dt [value]="products" [paginator]="true" [rows]="10" responsiveLayout="scroll">
<ng-template pTemplate="header">
<tr>
<th>ID</th>
<th>Name</th>
<th>Description</th>
<th>Price</th>
<th>Stock</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>{{ product.id }}</td>
<td>{{ product.name }}</td>
<td>{{ product.description }}</td>
<td>{{ product.price | currency: 'USD' }}</td>
<td>{{ product.stock }}</td>
</tr>
</ng-template>
</p-table>
<p-dialog header="Add Product" [(visible)]="displayDialog" [modal]="true" [responsive]="true">
<div class="p-fluid">
<div class="p-field">
<label for="name">Name</label>
<input id="name" type="text" pInputText [(ngModel)]="product.name" required />
<small class="p-error">Name is required.</small>
</div>
<div class="p-field">
<label for="description">Description</label>
<textarea id="description" pInputTextarea [(ngModel)]="product.description"></textarea>
</div>
<div class="p-field">
<label for="price">Price</label>
<input id="price" type="number" pInputText [(ngModel)]="product.price" />
</div>
<div class="p-field">
<label for="stock">Stock</label>
<input id="stock" type="number" pInputText [(ngModel)]="product.stock" />
</div>
</div>
</p-dialog>
</div>
product.scss
product.ts
import { Component, OnInit, signal, ViewChild } from '@angular/core';
import { ConfirmationService, MessageService } from 'primeng/api';
import { Table, TableModule } from 'primeng/table';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ButtonModule } from 'primeng/button';
import { RippleModule } from 'primeng/ripple';
import { ToastModule } from 'primeng/toast';
import { ToolbarModule } from 'primeng/toolbar';
import { RatingModule } from 'primeng/rating';
import { InputTextModule } from 'primeng/inputtext';
import { TextareaModule } from 'primeng/textarea';
import { SelectModule } from 'primeng/select';
import { Radi