Welcome to the Advanced Level of the Angular Shopping Cart Workshop! This level explores cutting-edge Angular features including the Resource API, advanced signal patterns, performance optimization, and production-ready cart analytics.
By completing this level, you will:
- Master the Resource API for advanced data fetching and caching
- Implement complex signal compositions and patterns
- Build production-ready cart analytics with performance monitoring
- Create optimized state synchronization across multiple services
- Understand advanced error handling and recovery patterns
- Implement real-time sync simulation and conflict resolution
- Build comprehensive export/import functionality
Primary Files:
src/app/advanced/services/product-resource.service.ts- STARTER FILE (Resource API)src/app/advanced/services/advanced-cart.service.ts- Advanced cart with analyticssrc/app/advanced/components/cart-advanced.component.ts- Feature-rich UI
Supporting Files:
src/app/advanced/components/product-search.component.ts- Advanced search componentsrc/app/advanced/services/product-resource.solution.ts- SOLUTION (reference)
The Advanced level introduces a sophisticated multi-service architecture:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Component Layer β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β CartAdvancedComponent β β
β β - Resource-driven data fetching β β
β β - Advanced filtering and pagination β β
β β - Real-time analytics dashboard β β
β β - Export/import functionality β β
β β - Performance monitoring UI β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ
β consumes resources & signals
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Service Layer β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β ProductResourceService β β
β β - Resource API for products β β
β β - Advanced filtering & pagination β β
β β - Caching and error handling β β
β β - Real-time product recommendations β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β AdvancedCartService β β
β β - Production-ready cart management β β
β β - Advanced analytics and metrics β β
β β - Session tracking and history β β
β β - Sync simulation and conflict resolution β β
β β - Export/import capabilities β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The Resource API provides a declarative way to manage async data:
// Traditional approach
private products$ = this.http.get<Product[]>('/api/products');
// Resource API approach
public readonly productsResource = resource<Product[], { search: string }>({
request: () => ({ search: this.searchQuery() }),
loader: async ({ request }) => {
const response = await this.http.get<Product[]>(`/api/products?search=${request.search}`).toPromise();
return response || [];
}
});- Start the development server:
npm start - Open your browser to
http://localhost:4200 - Click on "Advanced Level" in the navigation
- Explore the comprehensive interface with analytics panels
Goal: Create a sophisticated product fetching system with filtering, pagination, and caching.
Core Resource Structure:
export class ProductResourceService {
private searchQuery = signal<string>('');
private categoryFilter = signal<string>('all');
private priceRange = signal<{ min: number; max: number }>({ min: 0, max: 5000 });
private sortBy = signal<'name' | 'price' | 'rating'>('name');
private sortOrder = signal<'asc' | 'desc'>('asc');
private currentPage = signal<number>(1);
private itemsPerPage = signal<number>(12);
// TODO: Implement main products resource
public readonly productsResource = resource<ProductsResource, FiltersRequest>({
request: () => ({
search: this.searchQuery(),
category: this.categoryFilter(),
minPrice: this.priceRange().min,
maxPrice: this.priceRange().max,
sortBy: this.sortBy(),
sortOrder: this.sortOrder(),
page: this.currentPage(),
limit: this.itemsPerPage()
}),
loader: async ({ request }) => {
// TODO: Implement advanced filtering logic
// - Search by name, description, and tags
// - Filter by category and price range
// - Apply sorting (name, price, rating)
// - Handle pagination
// - Simulate network delay
// - Handle errors gracefully
}
});
}Requirements:
- Support complex filtering combinations
- Implement client-side pagination
- Add realistic network delay simulation (500ms)
- Handle empty results gracefully
- Implement error recovery with retry logic
Goal: Create a resource for individual product details with recommendations.
Implementation:
private selectedProductId = signal<string | null>(null);
public readonly selectedProductResource = resource<Product | null, { id: string | null }>({
request: () => ({ id: this.selectedProductId() }),
loader: async ({ request }) => {
if (!request.id) return null;
// TODO: Load single product by ID
// - Find product in mock data
// - Simulate API delay
// - Handle not found cases
}
});
public readonly recommendationsResource = resource<Product[], RecommendationRequest>({
request: () => ({
basedOnProductId: this.selectedProductId(),
category: this.categoryFilter()
}),
loader: async ({ request }) => {
// TODO: Generate intelligent product recommendations
// - Based on selected product (same category, similar tags)
// - Based on current category filter
// - Sort by rating
// - Return top 5 recommendations
}
});Goal: Build a production-ready cart service with comprehensive analytics and monitoring.
Core Features:
export class AdvancedCartService {
private cartState = signal<CartState>({
items: [],
lastUpdated: new Date(),
version: 1
});
private sessionStartTime = signal<Date>(new Date());
private cartHistory = signal<CartState[]>([]);
// TODO: Implement advanced computed analytics
public readonly cartAnalytics = computed<CartAnalytics>(() => {
// Calculate comprehensive analytics:
// - Total sessions
// - Average session value
// - Top categories
// - Abandonment rate
});
public readonly cartMetrics = computed(() => {
// Real-time performance metrics:
// - Session duration
// - Cart value per minute
// - Unique categories
// - Last modification time
// - Cart version for optimistic updates
});
// TODO: Implement cart sync resource
public readonly cartSyncResource = resource<SyncResult, { cartData: CartState }>({
request: () => ({ cartData: this.cartState() }),
loader: async ({ request }) => {
// TODO: Simulate server synchronization
// - API delay simulation
// - Occasional sync failures (10%)
// - Conflict resolution
// - Success/failure responses
}
});
}Goal: Create sophisticated cart operations with bulk actions and optimization.
Enhanced Operations:
// Advanced cart operations
duplicateItem(productId: string): void
moveToWishlist(productId: string): void
applyBulkDiscount(categoryOrAll: string, discountPercent: number): void
optimizeCart(): void // Remove duplicates, merge quantities
// Bulk operations
updateMultipleQuantities(updates: { productId: string; quantity: number }[]): void
// History operations
undoLastChange(): void
restoreCartFromHistory(historyIndex: number): void
// Export/Import
exportCart(): string
importCart(cartData: string): booleanGoal: Build comprehensive cart analytics with performance monitoring.
Analytics Features:
public readonly cartAnalytics = computed<CartAnalytics>(() => {
const history = this.cartHistory();
const currentSummary = this.cartSummary();
return {
totalSessions: history.length,
averageSessionValue: this.calculateAverageSessionValue(history),
topCategories: this.calculateTopCategories(),
abandonmentRate: this.calculateAbandonmentRate(history),
conversionMetrics: this.calculateConversionMetrics(),
performanceMetrics: this.calculatePerformanceMetrics()
};
});
// Real-time performance monitoring
public readonly cartMetrics = computed(() => {
const items = this.cartItems();
const summary = this.cartSummary();
const sessionDuration = Date.now() - this.sessionStartTime().getTime();
return {
itemCount: items.length,
uniqueCategories: new Set(items.map(item => item.category)).size,
averageItemPrice: items.length > 0 ? summary.totalPrice / items.length : 0,
sessionDurationMinutes: Math.floor(sessionDuration / (1000 * 60)),
cartValuePerMinute: sessionDuration > 0 ? summary.finalPrice / (sessionDuration / (1000 * 60)) : 0,
lastModified: this.cartState().lastUpdated,
cartVersion: this.cartState().version
};
});Goal: Connect all advanced features to a comprehensive user interface.
Component Features:
export class CartAdvancedComponent {
// Local state for UI controls
showFilters = signal<boolean>(false);
showAnalytics = signal<boolean>(false);
showHistory = signal<boolean>(false);
// Advanced operations
onExportCart(): void {
const cartData = this.cartService.exportCart();
// Create downloadable JSON file
}
onImportCart(event: Event): void {
const file = (event.target as HTMLInputElement).files?.[0];
// Handle file upload and cart import
}
onTriggerSync(): void {
this.cartService.triggerSync();
}
onOptimizeCart(): void {
this.cartService.optimizeCart();
}
// Advanced filtering
onComplexFilter(filters: {
search: string;
category: string;
priceMin: number;
priceMax: number;
sortBy: string;
sortOrder: string;
}): void {
// Apply multiple filters simultaneously
}
}Resource API Testing:
- β Products load with default filters
- β Search filtering works in real-time
- β Category filtering updates results
- β Price range filtering is accurate
- β Sorting by name, price, rating works
- β Pagination controls function correctly
- β Selected product loads correctly
- β Recommendations are relevant and accurate
- β Error states display appropriately
- β Loading states show during async operations
Advanced Cart Testing:
- β All basic cart operations (add, remove, update)
- β Bulk operations work correctly
- β Cart optimization removes duplicates
- β Undo/redo functionality works
- β Export creates valid JSON file
- β Import restores cart state correctly
- β Analytics calculate accurate metrics
- β Session tracking works across page refreshes
- β Sync simulation shows success/failure states
- β Performance metrics update in real-time
UI Testing:
- β Filter panel toggles correctly
- β Analytics panel shows comprehensive data
- β History panel displays past states
- β All buttons and controls are functional
- β Responsive design works on mobile
- β Loading states provide good UX
- β Error messages are user-friendly
public readonly productsResource = resource<ProductsResource, FiltersRequest>({
request: () => ({
search: this.searchQuery(),
category: this.categoryFilter(),
minPrice: this.priceRange().min,
maxPrice: this.priceRange().max,
sortBy: this.sortBy(),
sortOrder: this.sortOrder(),
page: this.currentPage(),
limit: this.itemsPerPage()
}),
loader: async ({ request }) => {
try {
// Simulate API call with filtering
const response = await this.http.get<Product[]>('/assets/data/products.json').toPromise();
let products = response || [];
// Apply complex filtering
if (request.search?.trim()) {
const searchLower = request.search.toLowerCase();
products = products.filter(p =>
p.name.toLowerCase().includes(searchLower) ||
p.description.toLowerCase().includes(searchLower) ||
p.tags?.some(tag => tag.toLowerCase().includes(searchLower))
);
}
if (request.category && request.category !== 'all') {
products = products.filter(p => p.category === request.category);
}
if (request.minPrice !== undefined && request.maxPrice !== undefined) {
products = products.filter(p =>
p.price >= request.minPrice! && p.price <= request.maxPrice!
);
}
// Apply sorting
products.sort((a, b) => {
let aValue: any, bValue: any;
switch (request.sortBy) {
case 'price':
aValue = a.price;
bValue = b.price;
break;
case 'rating':
aValue = a.rating;
bValue = b.rating;
break;
default:
aValue = a.name.toLowerCase();
bValue = b.name.toLowerCase();
}
const comparison = aValue < bValue ? -1 : aValue > bValue ? 1 : 0;
return request.sortOrder === 'desc' ? -comparison : comparison;
});
// Apply pagination
const startIndex = ((request.page || 1) - 1) * (request.limit || 12);
const endIndex = startIndex + (request.limit || 12);
const paginatedProducts = products.slice(startIndex, endIndex);
// Simulate network delay
await new Promise(resolve => setTimeout(resolve, 500));
return {
products: paginatedProducts,
loading: false,
error: null,
totalCount: products.length,
hasMore: endIndex < products.length
};
} catch (error) {
console.error('Error loading products:', error);
return {
products: [],
loading: false,
error: 'Failed to load products. Please try again.',
totalCount: 0,
hasMore: false
};
}
}
});public readonly cartAnalytics = computed<CartAnalytics>(() => {
const history = this.cartHistory();
const currentSummary = this.cartSummary();
const totalSessions = history.length;
const averageSessionValue = totalSessions > 0
? history.reduce((sum, state) => {
const sessionValue = state.items.reduce((itemSum, item) =>
itemSum + (item.price * item.quantity), 0);
return sum + sessionValue;
}, 0) / totalSessions
: 0;
// Category analysis
const categoryCount = new Map<string, number>();
this.cartItems().forEach(item => {
categoryCount.set(item.category, (categoryCount.get(item.category) || 0) + item.quantity);
});
const topCategories = Array.from(categoryCount.entries())
.map(([category, count]) => ({ category, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 3);
// Performance metrics
const sessionDuration = Date.now() - this.sessionStartTime().getTime();
const abandonmentRate = totalSessions > 0
? Math.max(0, (totalSessions - 1) / totalSessions * 100)
: 0;
return {
totalSessions,
averageSessionValue,
topCategories,
abandonmentRate,
sessionDurationMinutes: Math.floor(sessionDuration / (1000 * 60)),
cartValuePerMinute: sessionDuration > 0 ? currentSummary.finalPrice / (sessionDuration / (1000 * 60)) : 0,
conversionRate: this.calculateConversionRate(),
totalItemsHandled: this.calculateTotalItemsHandled(history)
};
});exportCart(): string {
return JSON.stringify({
state: this.cartState(),
analytics: this.cartAnalytics(),
metrics: this.cartMetrics(),
exportDate: new Date().toISOString(),
version: '1.0.0'
}, null, 2);
}
importCart(cartData: string): boolean {
try {
const data = JSON.parse(cartData);
// Validate import data structure
if (!data.state || !data.state.items || !Array.isArray(data.state.items)) {
throw new Error('Invalid cart data structure');
}
// Validate item structure
const isValidItem = (item: any): item is CartItem => {
return typeof item.id === 'string' &&
typeof item.productId === 'string' &&
typeof item.name === 'string' &&
typeof item.price === 'number' &&
typeof item.quantity === 'number';
};
if (!data.state.items.every(isValidItem)) {
throw new Error('Invalid cart item structure');
}
// Import the cart state
this.cartState.set({
...data.state,
lastUpdated: new Date(),
version: this.cartState().version + 1
});
console.log('Cart imported successfully:', {
itemCount: data.state.items.length,
importDate: data.exportDate,
version: data.version
});
return true;
} catch (error) {
console.error('Failed to import cart:', error);
return false;
}
}- Intelligent Caching: Resources automatically cache results based on request parameters
- Request Deduplication: Multiple components requesting the same data share results
- Fine-grained Updates: Only affected UI components re-render when data changes
- Memory Management: Automatic cleanup when components are destroyed
- Minimal Change Detection: Only signals and their dependents update
- Computed Optimization: Computed values only recalculate when dependencies change
- Effect Efficiency: Effects only run when their signal dependencies change
- Bundle Size: Smaller runtime footprint compared to RxJS
// Optimized effect with cleanup
effect(() => {
const items = this.cartItems();
// Debounce expensive operations
const timeoutId = setTimeout(() => {
this.performExpensiveAnalytics(items);
}, 500);
// Cleanup function
return () => clearTimeout(timeoutId);
});
// Error boundary pattern
public readonly safeCartSummary = computed(() => {
try {
return this.cartSummary();
} catch (error) {
console.error('Error calculating cart summary:', error);
return {
totalItems: 0,
totalPrice: 0,
totalDiscount: 0,
tax: 0,
finalPrice: 0
};
}
});You've successfully completed the Advanced Level when:
- β
All tests pass (
npm run test:advanced) - β Product resource loads and filters correctly
- β Advanced search and pagination work smoothly
- β Cart analytics provide meaningful insights
- β Export/import functionality works reliably
- β Sync simulation demonstrates success/failure states
- β Performance metrics update in real-time
- β Undo/redo operations work correctly
- β Bulk operations execute efficiently
- β Error handling provides good user experience
- β Responsive design works across all devices
- β Code follows production-ready patterns
Resource API Best Practices:
- Implement proper error boundaries
- Use request deduplication for performance
- Cache frequently accessed data
- Handle loading and error states gracefully
Signal Optimization:
- Minimize computed signal complexity
- Use effect cleanup for memory management
- Batch related signal updates
- Profile performance with Angular DevTools
State Management:
- Implement proper state versioning
- Handle concurrent user sessions
- Manage offline/online state synchronization
- Implement conflict resolution strategies
Error Handling:
- Comprehensive error logging
- User-friendly error messages
- Graceful degradation for failed operations
- Retry mechanisms for transient failures
Congratulations! You've completed a comprehensive journey through Angular's reactive programming evolution:
- Mastered RxJS Patterns: Built reactive state management with Observables
- Learned Signals Fundamentals: Migrated to modern reactive primitives
- Implemented Advanced Features: Created production-ready cart analytics
- Explored Resource API: Built sophisticated data fetching patterns
- Optimized Performance: Implemented fine-grained reactivity
Technical Skills:
- Angular Signals and computed values
- Resource API for data management
- Advanced state management patterns
- Performance optimization techniques
- Production-ready error handling
Architectural Skills:
- Reactive programming principles
- Service-based architecture design
- Separation of concerns
- Testable code patterns
- Scalable state management
Apply These Patterns:
- Migrate existing RxJS code to Signals gradually
- Implement Resource API for data-heavy applications
- Use advanced analytics patterns in production apps
- Apply performance optimization techniques
Continue Learning:
- Explore Angular's latest features and updates
- Study advanced reactive programming patterns
- Learn about micro-frontend architectures
- Investigate state management libraries integration
- Angular Signals RFC
- Resource API RFC
- Fine-grained Reactivity Principles
- Angular Performance Best Practices
π Congratulations on completing the Angular Shopping Cart Workshop!
You've successfully mastered the transition from RxJS to Signals and built a production-ready application with advanced features. These skills will serve you well in building modern, performant Angular applications.