ngOnchange doesn't detect dynamic changes

674 views Asked by At

I'm using angular for developing eCommerce app. I'm Using OnChange lifecycle hook to detect changes in the cart component and calculate total price. However, changes are not being detected.

Typescript Code:

import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { Device } from '../../shared/Device.model';

    @Component({
      selector: 'app-cart-calculator',
      changeDetection: ChangeDetectionStrategy.OnPush,
      templateUrl: './cart-calculator.component.html',
      styleUrls: ['./cart-calculator.component.scss']
    })
    export class CartCalculatorComponent implements OnChanges {
    
      @Input() cartProducts: Device[];
      @Input() quantities: number[];
    
      constructor() { }
    
      totalPrice: number = 0;
    
      ngOnInit(): void {
      }
    
      ngOnChanges() {
    
        this.totalPrice = 0;
        for (let i = 0; i < this.cartProducts.length; i++) {
          this.totalPrice += this.cartProducts[i].devicePrice * this.quantities[i];
          console.log(this.quantities);
        }
      }
    }

Template part:

<app-cart-calculator [quantities]="quantities" [cartProducts]="cartDevices"></app-cart-calculator>
3

There are 3 answers

1
Aakash Garg On

Array is an object in javascript so you have to pass new array every time you want it to be detected by ngOnChanges(). So for eg:- you pushed a new value to array like :-

       arr.push(4);

after that you need to give it a new reference :-

 arr = [...arr];
0
Florian On

You need to add SimpleChanges parameter to your implementation (ngOnChanges) of OnChanges.

ngOnChanges(changes: SimpleChanges) {      
  this.totalPrice = 0;
  const products = changes['cartProducts'] && changes['cartProducts'].currentValue;
  this.totalPrice = products.reduce((totalPrice, product) => totalPrice + (product.quantity * product.devicePrice), 0);
}

You also have reference problem, you are mutating your property cartDevices in your parent component instead of re-assigning its value.

0
Saptarsi On

ngOnChange - A lifecycle hook that is called when any data-bound property of a directive changes.

Here you are binding an array,and that is being treated as object.That means ngOnChange will be fire when you will send new Object,in our case case a new Array(means new reference id).

Angular uses the equality check operator === to detect when an input(s) to directives changes. The === operator checks for reference changes in the objects it's checking.

And here you are changing content of Object,Not Object reference.

ngOnChange will be triggered,in case of any kind of Object,when there will be a reference change means new Object.

So 1st solution is

this.cartDevices.push(device);
this.cartDevices = [...this.cartDevices];

Or form an array and assign that,by doing this it will change reference of this.cartDevices

this.cartDevices = devices

2nd Solution By binding a custom object with angular trackBy. angular will comapare that Object's equality by trackBy index rather object reference and this will improve performance also.

<app-cart-calculator [quantities]="quantities" [cartProducts]="{trackBy:cartDevices.length,data:cartDevices}"></app-cart-calculator>
  ngOnChanges() {
    this.totalPrice = 0;
    this.cartProducts = this.cartProducts.data;
    for (let i = 0; i < this.cartProducts.length; i++) {
      this.totalPrice += this.cartProducts[i].devicePrice * this.quantities[i];
      console.log(this.quantities);
    }
  }