Shopping Cart
A complete shopping cart implementation with add/remove items, quantity updates, and dynamic total calculations. Demonstrates reactive data management and user interaction patterns.
Features Demonstrated
Array manipulation - Add, remove, update cart items
Lambda expressions - Complex calculations directly in templates using
reduce,filter, etc.Event handling - Button clicks, quantity changes
Conditional rendering - Empty cart state, checkout button
List rendering with keys - Efficient cart item updates
Two-way binding - Quantity inputs
Number formatting - Currency display
Component state management - Cart as a service
Code
View Model (shopping-cart.ts)
export interface CartItem {
id: number;
productId: number;
name: string;
price: number;
quantity: number;
image: string;
maxQuantity: number;
}
export class ShoppingCart {
cartItems: CartItem[] = [];
// Add item to cart
addToCart(product: { id: number; name: string; price: number; image: string; maxQuantity: number }) {
const existingItem = this.cartItems.find(item => item.productId === product.id);
if (existingItem) {
// Increase quantity if item already in cart
if (existingItem.quantity < existingItem.maxQuantity) {
existingItem.quantity++;
} else {
alert(`Maximum quantity (${existingItem.maxQuantity}) reached for ${existingItem.name}`);
}
} else {
// Add new item
this.cartItems.push({
id: Date.now(), // Simple ID generation
productId: product.id,
name: product.name,
price: product.price,
quantity: 1,
image: product.image,
maxQuantity: product.maxQuantity
});
}
}
// Update item quantity
updateQuantity(item: CartItem, newQuantity: number) {
if (newQuantity <= 0) {
this.removeItem(item);
} else if (newQuantity <= item.maxQuantity) {
item.quantity = newQuantity;
} else {
item.quantity = item.maxQuantity;
alert(`Maximum quantity is ${item.maxQuantity}`);
}
}
// Increase quantity
increaseQuantity(item: CartItem) {
if (item.quantity < item.maxQuantity) {
item.quantity++;
} else {
alert(`Maximum quantity (${item.maxQuantity}) reached`);
}
}
// Decrease quantity
decreaseQuantity(item: CartItem) {
if (item.quantity > 1) {
item.quantity--;
} else {
this.removeItem(item);
}
}
// Remove item from cart
removeItem(item: CartItem) {
const index = this.cartItems.indexOf(item);
if (index > -1) {
this.cartItems.splice(index, 1);
}
}
// Clear entire cart
clearCart() {
if (confirm('Are you sure you want to clear your cart?')) {
this.cartItems = [];
}
}
// Proceed to checkout
checkout() {
console.log('Proceeding to checkout with:', this.cartItems);
alert('Proceeding to checkout...');
// In a real app, navigate to checkout page or open checkout modal
}
}Currency Value Converter (currency-value-converter.ts)
Template (shopping-cart.html)
Styles (shopping-cart.css)
How It Works
1. Lambda Expressions in Templates
Instead of computed properties in the view model, calculations are done directly in the template using lambda expressions:
Aurelia's lambda expressions support complex operations like reduce, filter, map, every, and some directly in templates. The template automatically tracks dependencies and recalculates when cartItems changes.
2. Array Manipulation
Using array methods ensures change detection:
3. Benefits of Lambda Expressions
Moving calculations to the template has several advantages:
Reduced boilerplate - No need for getter methods in the view model
Clear intent - Calculations are visible right where they're used
Single source of truth - The template directly expresses what data it needs
Automatic reactivity - Aurelia tracks all dependencies within lambda expressions
This approach is particularly useful for derived data that's only needed in the view.
4. Efficient List Updates
Using key: id allows Aurelia to track items efficiently:
When items are removed or reordered, Aurelia reuses DOM elements.
5. Quantity Validation
Multiple ways to update quantity with validation:
6. Conditional Rendering
Show different UI based on cart state:
Lambda expressions work seamlessly with conditionals: if.bind="cartItems.length" or if.bind="!cartItems.length".
7. Currency Formatting with Value Converter
The currency value converter formats prices consistently:
The converter uses Intl.NumberFormat for proper currency formatting including the currency symbol, decimal places, and thousands separators. This keeps formatting logic out of the view model.
8. When to Use Lambda Expressions vs Computed Properties
Use lambda expressions in templates when:
The calculation is only needed in the view
The logic is straightforward and readable inline
You want to reduce view model boilerplate
Use computed properties in the view model when:
The calculation is complex and would make the template hard to read
The value is used in multiple places (template and view model logic)
You need to unit test the calculation logic
The calculation is expensive and you want explicit memoization
For this shopping cart example, the calculations are simple arithmetic operations that are only displayed to the user, making lambda expressions a great fit. They eliminate boilerplate while keeping the template clear and maintainable.
Variations
Persist Cart to LocalStorage
Add Discount Codes
Cart as a Service
Make the cart available throughout the app:
Related
Last updated
Was this helpful?