0

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?

2 Answers 2

2

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>
Sign up to request clarification or add additional context in comments.

Comments

1

I think you could use observables and rxjs logic like this.

export class OrderSummary implements OnInit {
  product$: Observable<Product>;
  private productSubject$ = new Subject();

  @Input() set product(value: Product) {
    this.productSubject$.next(value);
  }
  
  ngOnInit(): void {
    this.product$ = this.productSubject$.pipe(
          distinctUntilChanged((prev: Product, curr: Product) => prev.ProductId === curr.ProductId), 
          map(prod => {
             this.generateOrderSummary(prod.orders);
             return prod;
          })
  }


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

}

and subscribe to product in your html

<div *ngIf="product$ | async as product">
   <b>{{product.ProductName}}</b>
   <b> ... show other product details </b>
   <table> ... show generated order summary ... </table>
</div>

4 Comments

Thanks Lautaro, I do not want to call generateOrderSummary when product name or other product properties change. How does it make sure it does not generateOrderSummary when product name change?
the method generateOrderSummary will be called only when the product id change (different product). distinctUntilChanged make the map not execute when product id is equals
The product id will always remain the same. Other properties inside the product object may change.
I think I can tweak the logic to make it work so thanks for your help too.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.