Hone logo
Hone
Problems

Reacting to Input Changes in Angular with ngOnChanges

Angular components often need to respond dynamically to changes in their input properties. The ngOnChanges lifecycle hook provides a robust mechanism for this, allowing you to execute logic whenever one or more of your component's @Input() properties change. This challenge will test your understanding of how to correctly implement and utilize ngOnChanges to manage component state based on external data.

Problem Description

You are tasked with creating an Angular component called ProductDisplayComponent. This component will receive a product object as an @Input() property. The product object will have at least two properties: name (string) and price (number).

Your goal is to implement the ngOnChanges lifecycle hook within ProductDisplayComponent to:

  1. Log changes: Whenever the product input property changes, log a message to the console indicating which property changed and its new value.
  2. Update internal state (if necessary): If the price property changes, update an internal component property called discountedPrice to be 90% of the new price. If the product object itself is reassigned (e.g., a completely new product is passed), reset discountedPrice to the product's current price.
  3. Initial display: Ensure that when the component first loads with a product, the discountedPrice is correctly initialized.

Key Requirements:

  • Implement the OnChanges interface.
  • Correctly use the ngOnChanges method.
  • Access the SimpleChanges object passed to ngOnChanges.
  • Distinguish between changes to individual properties within the product object and the reassignment of the entire product object.
  • The component should have an @Input() property named product of type { name: string; price: number; }.
  • The component should have an internal property discountedPrice of type number.

Expected Behavior:

  • When the product input is initially set, ngOnChanges will be called, and discountedPrice will be initialized.
  • If only the price of an existing product object changes (e.g., componentInstance.product.price = newPrice), ngOnChanges will be called, and discountedPrice will be updated to 90% of the new price.
  • If the entire product object is replaced (e.g., componentInstance.product = newProductObject), ngOnChanges will be called, and discountedPrice will be reset to the price of the new product.
  • The console logs should clearly indicate what changed.

Edge Cases:

  • What happens if the product input is null or undefined initially? The component should gracefully handle this without errors.
  • What happens if the price property of the product object is not a valid number? (For this challenge, assume valid number inputs for price, but be aware of this in real-world scenarios).

Examples

Let's assume we have a parent component that passes data to ProductDisplayComponent.

Example 1: Initial Load and Price Change

  • Parent Component Action:

    • Sets productInput = { name: 'Laptop', price: 1200 }.
    • Later, changes productInput = { name: 'Laptop', price: 1100 } (only price changed).
  • ProductDisplayComponent Behavior:

    • Initial call to ngOnChanges:
      Console Output: ProductDisplayComponent: ngOnChanges called.
      Console Output: ProductDisplayComponent: Initial product set: {"name":"Laptop","price":1200}
      
      discountedPrice is initialized to 1200.
    • Second call to ngOnChanges (after price change):
      Console Output: ProductDisplayComponent: ngOnChanges called.
      Console Output: ProductDisplayComponent: product changed. Previous: {"name":"Laptop","price":1200}, Current: {"name":"Laptop","price":1100}
      Console Output: ProductDisplayComponent: Price changed. Old price: 1200, New price: 1100
      
      discountedPrice is updated to 1100 * 0.9 = 990.

Example 2: Product Reassignment

  • Parent Component Action:

    • Sets productInput = { name: 'Keyboard', price: 75 }.
    • Later, changes productInput = { name: 'Mouse', price: 25 } (entire product object replaced).
  • ProductDisplayComponent Behavior:

    • Initial call to ngOnChanges:
      Console Output: ProductDisplayComponent: ngOnChanges called.
      Console Output: ProductDisplayComponent: Initial product set: {"name":"Keyboard","price":75}
      
      discountedPrice is initialized to 75.
    • Second call to ngOnChanges (after product reassignment):
      Console Output: ProductDisplayComponent: ngOnChanges called.
      Console Output: ProductDisplayComponent: product changed. Previous: {"name":"Keyboard","price":75}, Current: {"name":"Mouse","price":25}
      Console Output: ProductDisplayComponent: Product object reassigned.
      
      discountedPrice is reset to 25.

Example 3: Initial null product, then valid product

  • Parent Component Action:

    • Sets productInput = null.
    • Later, sets productInput = { name: 'Monitor', price: 300 }.
  • ProductDisplayComponent Behavior:

    • First call to ngOnChanges:
      Console Output: ProductDisplayComponent: ngOnChanges called.
      Console Output: ProductDisplayComponent: Initial product set: null
      
      discountedPrice remains undefined or 0 (depending on initial declaration).
    • Second call to ngOnChanges:
      Console Output: ProductDisplayComponent: ngOnChanges called.
      Console Output: ProductDisplayComponent: product changed. Previous: null, Current: {"name":"Monitor","price":300}
      Console Output: ProductDisplayComponent: Initial product set: {"name":"Monitor","price":300}
      
      discountedPrice is initialized to 300.

Constraints

  • The ProductDisplayComponent must be a standard Angular component.
  • The product input property must be decorated with @Input().
  • The ngOnChanges method signature must be correct.
  • The SimpleChanges object provided to ngOnChanges must be utilized to determine property changes.
  • Performance is not a critical constraint for this challenge, but avoid overly inefficient operations.

Notes

  • The SimpleChanges object is a key-value pair where keys are the names of input properties that changed, and values are SimpleChange objects.
  • A SimpleChange object has properties: previousValue, currentValue, and firstChange (boolean).
  • When an @Input() property is changed, ngOnChanges is called before ngOnInit (if it's the first change) and before ngDoCheck.
  • Consider how you will differentiate between a change to a property within the product object versus the product object itself being replaced. The currentValue and previousValue in SimpleChanges can help here. If product is an object, the previousValue and currentValue might be the same object reference if only a nested property changed, but different references if the object itself was reassigned. A more robust way to detect internal changes is to check if changes['product'].currentValue and changes['product'].previousValue are different instances or if changes['product'].firstChange is true.
  • The product input can be null or undefined. Ensure your component handles this scenario gracefully.
  • For logging, use console.log().
Loading editor...
typescript