Product Catalog
Features Demonstrated
Code
View Model (product-catalog.ts)
interface Product {
id: number;
name: string;
description: string;
price: number;
category: string;
image: string;
inStock: boolean;
rating: number;
}
type SortOption = 'name' | 'price-low' | 'price-high' | 'rating';
export class ProductCatalog {
// Data
products: Product[] = [
{
id: 1,
name: 'Wireless Headphones',
description: 'Premium noise-canceling headphones with 30-hour battery',
price: 299.99,
category: 'Audio',
image: '/images/headphones.jpg',
inStock: true,
rating: 4.5
},
{
id: 2,
name: 'Smart Watch',
description: 'Fitness tracking with heart rate monitor and GPS',
price: 399.99,
category: 'Wearables',
image: '/images/smartwatch.jpg',
inStock: true,
rating: 4.2
},
{
id: 3,
name: 'Laptop Stand',
description: 'Ergonomic aluminum stand for better posture',
price: 49.99,
category: 'Accessories',
image: '/images/stand.jpg',
inStock: false,
rating: 4.8
},
{
id: 4,
name: 'Mechanical Keyboard',
description: 'RGB backlit with customizable switches',
price: 159.99,
category: 'Accessories',
image: '/images/keyboard.jpg',
inStock: true,
rating: 4.6
},
{
id: 5,
name: 'USB-C Hub',
description: '7-in-1 adapter with 4K HDMI and SD card reader',
price: 79.99,
category: 'Accessories',
image: '/images/hub.jpg',
inStock: true,
rating: 4.3
},
{
id: 6,
name: 'Wireless Earbuds',
description: 'True wireless with active noise cancellation',
price: 199.99,
category: 'Audio',
image: '/images/earbuds.jpg',
inStock: true,
rating: 4.4
}
];
// Filter state
searchQuery = '';
selectedCategories: string[] = [];
sortBy: SortOption = 'name';
showOutOfStock = true;
// Computed property for unique categories
get categories(): string[] {
return [...new Set(this.products.map(p => p.category))].sort();
}
// Computed property for filtered and sorted products
get filteredProducts(): Product[] {
let filtered = this.products;
// Filter by search query
if (this.searchQuery.trim()) {
const query = this.searchQuery.toLowerCase();
filtered = filtered.filter(p =>
p.name.toLowerCase().includes(query) ||
p.description.toLowerCase().includes(query)
);
}
// Filter by selected categories
if (this.selectedCategories.length > 0) {
filtered = filtered.filter(p =>
this.selectedCategories.includes(p.category)
);
}
// Filter out of stock if needed
if (!this.showOutOfStock) {
filtered = filtered.filter(p => p.inStock);
}
// Sort products
return this.sortProducts(filtered);
}
get hasActiveFilters(): boolean {
return this.searchQuery.trim() !== '' ||
this.selectedCategories.length > 0 ||
!this.showOutOfStock;
}
private sortProducts(products: Product[]): Product[] {
const sorted = [...products];
switch (this.sortBy) {
case 'name':
return sorted.sort((a, b) => a.name.localeCompare(b.name));
case 'price-low':
return sorted.sort((a, b) => a.price - b.price);
case 'price-high':
return sorted.sort((a, b) => b.price - a.price);
case 'rating':
return sorted.sort((a, b) => b.rating - a.rating);
default:
return sorted;
}
}
clearFilters() {
this.searchQuery = '';
this.selectedCategories = [];
this.showOutOfStock = true;
}
setSortOrder(sortOption: SortOption) {
this.sortBy = sortOption;
}
}Template (product-catalog.html)
Styles (product-catalog.css)
How It Works
1. Search with Debouncing
2. Reactive Filtering
3. Multiple Checkbox Selection
4. Efficient List Rendering
5. Dynamic CSS Classes
Variations
Add Price Range Filter
Add to Cart Functionality
Persist Filters in URL
Related
Last updated
Was this helpful?