Skip to main content

Overview

This example demonstrates how to build a full e-commerce experience including product browsing, filtering, cart management, and checkout confirmation. It showcases best practices for integrating multiple Magary components into a cohesive shopping flow.
Time to integrate: Less than 1 dayCreate a production-ready e-commerce interface with product carousel, filters, cart, and dialogs.

Key Features

  • Featured products carousel
  • Product grid with filtering and search
  • Product detail dialogs
  • Shopping cart with inline quantity editing
  • Stock status indicators
  • Add-to-cart confirmation toasts
  • Responsive layout

Components Used

MagaryCarousel

Showcase featured products

MagaryInput

Search and quantity fields

MagarySelect

Category and sort filters

MagaryTable

Shopping cart display

MagaryDialog

Product detail modals

MagaryButton

Action triggers

MagaryTag

Stock status badges

MagaryToast

User feedback notifications

Project Structure

Organize your e-commerce feature with this structure:
src/app/ecommerce/
  ecommerce.page.ts
  ecommerce.page.html
  ecommerce.page.scss
  ecommerce.models.ts
  ecommerce.service.ts

Data Models

Define your product and cart models:
ecommerce.models.ts
export interface Product {
  id: string;
  name: string;
  category: string;
  price: number;
  stock: 'INSTOCK' | 'LOWSTOCK' | 'OUTOFSTOCK';
  image: string;
  description?: string;
  rating?: number;
}

export interface CartLine {
  productId: string;
  name: string;
  quantity: number;
  price: number;
  image: string;
}

export interface Category {
  label: string;
  value: string;
}

Component Implementation

import { ChangeDetectionStrategy, Component, signal, computed } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import {
  MagaryButton,
  MagaryCarousel,
  MagaryDialog,
  MagaryInput,
  MagarySelect,
  MagaryTable,
  MagaryTag,
  MagaryToast,
  MessageService,
} from 'ng-magary';
import { Product, CartLine, Category } from './ecommerce.models';

@Component({
  selector: 'app-ecommerce-page',
  standalone: true,
  imports: [
    CommonModule,
    FormsModule,
    MagaryCarousel,
    MagarySelect,
    MagaryInput,
    MagaryTable,
    MagaryDialog,
    MagaryButton,
    MagaryTag,
    MagaryToast,
  ],
  providers: [MessageService],
  templateUrl: './ecommerce.page.html',
  styleUrls: ['./ecommerce.page.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class EcommercePage {
  constructor(private messageService: MessageService) {}

  // Search and filters
  readonly search = signal('');
  readonly selectedCategory = signal<string | null>(null);
  readonly selectedSort = signal('name');

  // Product data
  readonly allProducts = signal<Product[]>([
    {
      id: '1',
      name: 'Wireless Headphones',
      category: 'electronics',
      price: 99.99,
      stock: 'INSTOCK',
      image: 'assets/products/headphones.jpg',
      description: 'Premium noise-cancelling wireless headphones with 30-hour battery life.',
      rating: 4.5,
    },
    {
      id: '2',
      name: 'Smart Watch',
      category: 'electronics',
      price: 299.99,
      stock: 'LOWSTOCK',
      image: 'assets/products/smartwatch.jpg',
      description: 'Advanced fitness tracking with heart rate monitor and GPS.',
      rating: 4.8,
    },
    {
      id: '3',
      name: 'Laptop Backpack',
      category: 'accessories',
      price: 49.99,
      stock: 'INSTOCK',
      image: 'assets/products/backpack.jpg',
      description: 'Durable laptop backpack with USB charging port and water-resistant material.',
      rating: 4.2,
    },
    {
      id: '4',
      name: 'Mechanical Keyboard',
      category: 'electronics',
      price: 129.99,
      stock: 'OUTOFSTOCK',
      image: 'assets/products/keyboard.jpg',
      description: 'RGB mechanical gaming keyboard with hot-swappable switches.',
      rating: 4.6,
    },
    {
      id: '5',
      name: 'Desk Lamp',
      category: 'home',
      price: 39.99,
      stock: 'INSTOCK',
      image: 'assets/products/lamp.jpg',
      description: 'LED desk lamp with adjustable brightness and color temperature.',
      rating: 4.3,
    },
  ]);

  // Featured products for carousel
  readonly featuredProducts = computed(() => 
    this.allProducts().filter(p => p.rating && p.rating >= 4.5)
  );

  // Filtered products based on search and category
  readonly filteredProducts = computed(() => {
    let products = this.allProducts();
    
    const searchTerm = this.search().toLowerCase();
    if (searchTerm) {
      products = products.filter(p => 
        p.name.toLowerCase().includes(searchTerm) ||
        p.description?.toLowerCase().includes(searchTerm)
      );
    }

    const category = this.selectedCategory();
    if (category) {
      products = products.filter(p => p.category === category);
    }

    // Apply sorting
    const sort = this.selectedSort();
    products = [...products].sort((a, b) => {
      if (sort === 'name') return a.name.localeCompare(b.name);
      if (sort === 'price-asc') return a.price - b.price;
      if (sort === 'price-desc') return b.price - a.price;
      if (sort === 'rating') return (b.rating || 0) - (a.rating || 0);
      return 0;
    });

    return products;
  });

  // Shopping cart
  readonly cartItems = signal<CartLine[]>([]);
  readonly cartTotal = computed(() => 
    this.cartItems().reduce((sum, item) => sum + (item.price * item.quantity), 0)
  );

  // Dialog state
  readonly productDialogVisible = signal(false);
  readonly selectedProduct = signal<Product | null>(null);
  readonly cartDialogVisible = signal(false);

  // Filter options
  readonly categories: Category[] = [
    { label: 'All Categories', value: '' },
    { label: 'Electronics', value: 'electronics' },
    { label: 'Accessories', value: 'accessories' },
    { label: 'Home', value: 'home' },
  ];

  readonly sortOptions = [
    { label: 'Name', value: 'name' },
    { label: 'Price: Low to High', value: 'price-asc' },
    { label: 'Price: High to Low', value: 'price-desc' },
    { label: 'Rating', value: 'rating' },
  ];

  showProductDetail(product: Product): void {
    this.selectedProduct.set(product);
    this.productDialogVisible.set(true);
  }

  addToCart(product: Product, quantity: number = 1): void {
    const cart = this.cartItems();
    const existingItem = cart.find(item => item.productId === product.id);

    if (existingItem) {
      existingItem.quantity += quantity;
      this.cartItems.set([...cart]);
    } else {
      this.cartItems.set([
        ...cart,
        {
          productId: product.id,
          name: product.name,
          quantity,
          price: product.price,
          image: product.image,
        },
      ]);
    }

    this.messageService.add({
      severity: 'success',
      summary: 'Added to Cart',
      detail: `${product.name} has been added to your cart.`,
    });

    this.productDialogVisible.set(false);
  }

  updateCartQuantity(cartLine: CartLine, quantity: number): void {
    if (quantity <= 0) {
      this.removeFromCart(cartLine);
      return;
    }

    const cart = this.cartItems();
    const item = cart.find(i => i.productId === cartLine.productId);
    if (item) {
      item.quantity = quantity;
      this.cartItems.set([...cart]);
    }
  }

  removeFromCart(cartLine: CartLine): void {
    this.cartItems.set(
      this.cartItems().filter(item => item.productId !== cartLine.productId)
    );

    this.messageService.add({
      severity: 'info',
      summary: 'Removed from Cart',
      detail: `${cartLine.name} has been removed from your cart.`,
    });
  }

  getStockSeverity(stock: Product['stock']): string {
    switch (stock) {
      case 'INSTOCK': return 'success';
      case 'LOWSTOCK': return 'warning';
      case 'OUTOFSTOCK': return 'danger';
    }
  }

  getStockLabel(stock: Product['stock']): string {
    switch (stock) {
      case 'INSTOCK': return 'In Stock';
      case 'LOWSTOCK': return 'Low Stock';
      case 'OUTOFSTOCK': return 'Out of Stock';
    }
  }
}

Layout Diagram

┌─────────────────────────────────────────────────────┐
│ Our Products                          🛒 Cart (3)   │
├─────────────────────────────────────────────────────┤
│ Featured Products                                   │
│ [←] ┌──────────┐ ┌──────────┐ ┌──────────┐ [→]    │
│     │ Product  │ │ Product  │ │ Product  │        │
│     │  Image   │ │  Image   │ │  Image   │        │
│     │  $99.99  │ │ $299.99  │ │ $129.99  │        │
│     │  ★★★★★   │ │  ★★★★★   │ │  ★★★★☆   │        │
│     └──────────┘ └──────────┘ └──────────┘        │
├─────────────────────────────────────────────────────┤
│ [🔍 Search...] [Category ▼] [Sort by ▼]           │
├─────────────────────────────────────────────────────┤
│ ┌────────────┐ ┌────────────┐ ┌────────────┐      │
│ │   Product  │ │   Product  │ │   Product  │      │
│ │    Image   │ │    Image   │ │    Image   │      │
│ │            │ │            │ │            │      │
│ │  Product 1 │ │  Product 2 │ │  Product 3 │      │
│ │ Description│ │ Description│ │ Description│      │
│ │  $99.99    │ │  $49.99    │ │  $39.99    │      │
│ │ [In Stock] │ │ [Low Stock]│ │ [In Stock] │      │
│ └────────────┘ └────────────┘ └────────────┘      │
└─────────────────────────────────────────────────────┘

User Flow

1

Browse Products

Users land on the page and see featured products in a carousel at the top
2

Search and Filter

Users can search by keyword, filter by category, and sort results
3

View Details

Clicking a product opens a dialog with full details, images, and ratings
4

Add to Cart

Users add products to cart and receive a confirmation toast notification
5

Review Cart

Users can open the cart dialog to review items, adjust quantities, or remove products
6

Checkout

Users proceed to checkout with a summary of their cart total

Accessibility Checklist

  • Product cards are keyboard accessible with Tab/Shift+Tab
  • Enter or Space opens product detail dialog
  • Escape closes dialogs and returns focus
  • Carousel navigation works with arrow keys
  • All inputs have associated labels
  • Search input provides placeholder text
  • Quantity spinners work with keyboard up/down arrows
  • Select dropdowns announce options to screen readers
  • Product images have descriptive alt text
  • Price changes are announced to screen readers
  • Stock status uses semantic color coding plus text labels
  • Toast notifications are announced automatically
  • Focus indicators visible on all interactive elements
  • Hover states provide visual affordance
  • Disabled states clearly communicated (out of stock)
  • Loading states prevent multiple submissions

Best Practices

State Management: Use signals with computed values to automatically update filtered products when search or category changes.
Stock Validation: Always validate stock availability before allowing add-to-cart actions. Disable buttons for out-of-stock items.
Responsive Design: The product grid uses CSS Grid’s auto-fill to automatically adjust columns based on available space.
User Feedback: Use toast notifications for all cart actions to provide immediate confirmation to users.

Next Steps

Payment Integration

Add Stripe or PayPal checkout integration

Wishlist Feature

Allow users to save products for later

Product Reviews

Enable customer reviews and ratings

Inventory Sync

Connect to real-time inventory system

Build docs developers (and LLMs) love