Angular State Management: The Shopping Cart Challenge
Modern web applications often require sophisticated ways to manage data that is shared across multiple components. This is especially true for features like shopping carts, where items need to be added, removed, and their quantities updated, with these changes reflecting instantly across the entire application. This challenge focuses on implementing a robust state management solution for a simulated shopping cart in an Angular application.
Problem Description
Your task is to implement a shopping cart feature in an Angular application using a chosen state management library or pattern. You will need to create components that display product listings, allow users to add items to the cart, and a separate component that displays the current contents of the shopping cart. All changes to the cart should be managed centrally and reflected across all relevant components without explicit prop drilling.
Key Requirements:
- Product Listing Component: Display a list of available products. Each product should have a name, price, and an "Add to Cart" button.
- Shopping Cart Component: Display the current items in the shopping cart. Each item should show its name, quantity, and a subtotal (price * quantity). The component should also display a grand total for all items in the cart.
- State Management: Implement a mechanism to manage the shopping cart's state. This state should include the list of items currently in the cart, along with their quantities.
- Actions:
- Adding an item to the cart: If the item is already in the cart, increment its quantity. Otherwise, add it with a quantity of 1.
- Removing an item from the cart: Decrease the quantity of an item. If the quantity reaches 0, remove the item entirely from the cart.
- Data Flow: Changes to the shopping cart state should automatically update the Shopping Cart Component. The "Add to Cart" button in the Product Listing Component should trigger state changes.
- No Prop Drilling: State changes and data retrieval for the cart should be handled by the state management solution, not by passing data through component inputs and outputs.
Expected Behavior:
- When a user clicks "Add to Cart" for a product:
- If the product is not in the cart, it should be added with a quantity of 1.
- If the product is already in the cart, its quantity should increase by 1.
- The Shopping Cart Component should update to reflect the new item or updated quantity.
- When a user clicks a "Remove" button (or similar action) for an item in the Shopping Cart Component:
- The quantity of that item should decrease by 1.
- If the quantity becomes 0, the item should be removed from the cart.
- The Shopping Cart Component should update to reflect the changes.
- The grand total displayed in the Shopping Cart Component should always reflect the sum of all subtotals.
Edge Cases:
- Adding the same product multiple times.
- Removing a product until its quantity reaches zero.
- Displaying an empty cart state.
Examples
Example 1: Adding a new item
Initial State: Cart is empty. Products available: [{ id: 1, name: 'Apple', price: 0.5 }]
User Action: Clicks "Add to Cart" for 'Apple' in the Product Listing Component.
Output (Shopping Cart Component display):
Items:
- Apple: Quantity 1, Subtotal: $0.50
Grand Total: $0.50
Example 2: Adding an existing item
Initial State: Cart contains: [{ id: 1, name: 'Apple', price: 0.5, quantity: 1 }]
User Action: Clicks "Add to Cart" for 'Apple' again.
Output (Shopping Cart Component display):
Items:
- Apple: Quantity 2, Subtotal: $1.00
Grand Total: $1.00
Example 3: Removing an item to zero quantity
Initial State: Cart contains: [{ id: 1, name: 'Apple', price: 0.5, quantity: 1 }]
User Action: Clicks "Remove" for 'Apple' in the Shopping Cart Component.
Output (Shopping Cart Component display):
Items:
(No items displayed)
Grand Total: $0.00
Constraints
- Your solution must be implemented in TypeScript.
- You can choose any established Angular-compatible state management library (e.g., NgRx, Akita, Elf) or implement a custom service-based solution with RxJS for observable state.
- The product data can be hardcoded for this challenge.
- Focus on the state management logic and its integration with components.
Notes
- Consider how you will structure your state (e.g., an array of cart items, each with an ID, name, price, and quantity).
- Think about how to represent the actions (e.g., an
Actionenum or class if using a library like NgRx). - RxJS
BehaviorSubjectorSubjectin a service can be a powerful tool for implementing a custom state management solution. - Success will be measured by the clean separation of concerns, the maintainability of the code, and the correct, reactive updating of the UI based on state changes.