Hone logo
Hone
Problems

Angular Feature State Management Challenge

This challenge focuses on implementing robust feature state management within an Angular application. Effectively managing the state of specific features is crucial for building scalable and maintainable Angular applications, especially as they grow in complexity. You will demonstrate your ability to organize and access data related to a particular feature in a clean and decoupled manner.

Problem Description

Your task is to create a feature state management solution for a hypothetical "Product Catalog" feature in an Angular application. This state should encapsulate all information related to products, such as a list of products, the currently selected product, and loading/error states associated with fetching product data.

Key Requirements:

  1. Feature Store: Create a dedicated "store" for the Product Catalog feature. This store should hold the state for products.
  2. State Properties: The Product Catalog state should include:
    • products: An array of product objects (you can define a simple Product interface for this).
    • selectedProductId: The ID of the currently selected product (or null if none is selected).
    • isLoading: A boolean indicating if product data is currently being fetched.
    • error: A string or null representing any error message encountered during data fetching.
  3. Selectors: Implement selectors to retrieve specific pieces of state from the Product Catalog store. Examples include:
    • selectAllProducts: Returns the entire products array.
    • selectIsLoading: Returns the isLoading boolean.
    • selectError: Returns the error string or null.
    • selectSelectedProduct: Returns the full product object for the selectedProductId, or null if no product is selected or found.
  4. Actions/Mutations (Conceptual): While you won't implement a full Redux-like action system, conceptually demonstrate how you would trigger state changes. This can be done by creating simple methods within your state management service to update the state. For example:
    • loadProducts(): Sets isLoading to true and simulates fetching products.
    • setProducts(products: Product[]): Updates the products array and sets isLoading to false.
    • setProductLoadingError(error: string): Sets the error and isLoading to false.
    • selectProduct(productId: number): Updates selectedProductId.
  5. Service Integration: Create an Angular service that utilizes this feature state. This service should expose methods to interact with the state (e.g., through the selectors) and trigger state mutations.

Expected Behavior:

  • When loadProducts() is called, isLoading should become true.
  • When setProducts() is called with a list of products, the products state should be updated, and isLoading should become false.
  • When setProductLoadingError() is called, the error state should be updated, and isLoading should become false.
  • selectAllProducts should always return the current array of products.
  • selectSelectedProduct should correctly return the product corresponding to selectedProductId or null.

Edge Cases to Consider:

  • What happens if selectSelectedProduct is called when no product is selected?
  • What happens if selectSelectedProduct is called with an ID that doesn't exist in the products array?
  • How does the state handle concurrent loading attempts (though for this challenge, simple sequential updates are sufficient).

Examples

Example 1: Initial State and Loading

Input:
- Call `productCatalogStateService.loadProducts()`

Output (conceptual state changes):
{
  products: [],
  selectedProductId: null,
  isLoading: true,
  error: null
}

Explanation:
Initially, the state is empty. Calling `loadProducts` sets `isLoading` to true to indicate that data fetching is in progress.

Example 2: Data Successfully Loaded

Input:
- Call `productCatalogStateService.loadProducts()`
- Assume the actual product data fetched is:
  [
    { id: 1, name: 'Laptop', price: 1200 },
    { id: 2, name: 'Keyboard', price: 75 }
  ]
- Call `productCatalogStateService.setProducts(fetchedProducts)`

Output (conceptual state changes):
{
  products: [
    { id: 1, name: 'Laptop', price: 1200 },
    { id: 2, name: 'Keyboard', price: 75 }
  ],
  selectedProductId: null,
  isLoading: false,
  error: null
}

Explanation:
After the products are fetched and set, the `products` array is populated, and `isLoading` is reset to false.

Example 3: Selecting a Product

Input:
- State as in Example 2
- Call `productCatalogStateService.selectProduct(1)`

Output (conceptual state changes):
{
  products: [
    { id: 1, name: 'Laptop', price: 1200 },
    { id: 2, name: 'Keyboard', price: 75 }
  ],
  selectedProductId: 1,
  isLoading: false,
  error: null
}

Using `productCatalogStateService.selectSelectedProduct()` would return:
{ id: 1, name: 'Laptop', price: 1200 }

Explanation:
The `selectedProductId` is updated to 1. The `selectSelectedProduct` selector now returns the full product object for ID 1.

Example 4: Error Handling

Input:
- Call `productCatalogStateService.loadProducts()`
- Assume an error occurs during fetch, and the error message is "Network Error"
- Call `productCatalogStateService.setProductLoadingError("Network Error")`

Output (conceptual state changes):
{
  products: [], // Or previous products if not reset on error
  selectedProductId: null,
  isLoading: false,
  error: "Network Error"
}

Explanation:
The `error` state is populated, and `isLoading` is set to false, indicating the loading process has completed with an error.

Constraints

  • You are expected to use TypeScript for all implementations.
  • The solution should be designed to be testable.
  • Avoid using external state management libraries like NgRx or Akita. Implement the core logic yourself using Angular services and RxJS subjects/behavior subjects for state observation.
  • Focus on clarity and maintainability of the state management pattern.

Notes

  • Consider using BehaviorSubject from RxJS to manage your state, as it allows components to subscribe and immediately receive the current state.
  • Think about how components would subscribe to these state changes and react to them.
  • The Product interface can be as simple as { id: number; name: string; price: number; }.
  • The "service integration" part means creating a service that uses your state management logic, not necessarily building a full feature service from scratch. The focus is on how that service interacts with and exposes the feature state.
Loading editor...
typescript