Search code examples
angulartypescriptangular2-changedetection

Change detection for child property in Angular


I have the following Product object inside an Angular application. The product contains array of orders and other product information.

Product.ts

export interface Product { 
    ProductID: number;
    ProductName: string;
    Orders: Order[]
}

I need to display product information and generate order summary inside a child angular component.

order-summary.component.ts

export class OrderSummary {

  _product: Product;
  @Input() set product(value: Product) {
    this._product = value;
    this.generateOrderSummary(value.Orders);
  }

  private generateOrderSummary(orders: Order[]) {
   // Logic to generate order summary
  }

}

order-summary.component.html

<b>{{_product.ProductName}}</b>
<b> ... show other product details </b>
<table> ... show generated order summary ... </table>

I believe every time the product object changes, it will generate the order summary (generateOrderSummary) even if the Orders array hasn't been changed. What is a better way to fix this issue?

I am thinking of adding another property for Orders[] so I can regenerate summary only when order has been changed. However, product already contains orders not sure if it is a good design.

order-summary.component.ts

export class OrderSummary {
  _product: Product;
  @Input() set product(value: Product) {
    this._product = value;
  }

  @Input() set orders(value: Orders[]) {
    this.generateOrderSummary(value.Orders);
  }

  private generateOrderSummary(orders: Order[]) {
   // Logic to generate order summary
  }
}

Are there any better solutions?


Solution

  • In this case you could skip the setter implementation of the @Input and use Angular OnChanges hook with SimpleChanges parameter. It allows you to check if it's the first invoke of the component and compare previous and current values of the @Input property.

    Try the following

    order-summary.component.ts

    import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
    
    export class OrderSummary implements OnChanges {
      @Input() product: Product;
    
      ngOnChanges(changes: SimpleChanges) {
        if (!!changes.product) {
          if (changes.product.firstChange) {                 // <-- first time the component is rendered
            this.generateOrderSummary(this.product.orders);  // <-- could also be `changes.product.currentValue.orders`
          } else if (                                        // <-- true only if different `orders` value
            !!changes.product.previousValue &&
            !!changes.product.previousValue.orders &&
            changes.product.previousValue.orders !== changes.product.currentValue.orders
          ) {
            this.generateOrderSummary(product.orders);
          }
        }
      }
    
      private generateOrderSummary(orders: Order[]) {
        // Logic to generate order summary
      }
    }
    

    order-summary.component.html

    <b>{{ product?.ProductName }}</b>
    <b> ... show other product details </b>
    <table> ... show generated order summary ... </table>