1

I have an object from parent component also received in a child component similar to this:

{
  attribute: 'aaaa',
  attribute2: [
    {
      value
    },
    {
      value
    },
    {
      value
    },
  ]
}

This object is an @Input from a parent component. When I make changes to the objects inside the attribute2 array, I would like the child component detect that changes were made and then gets updated. As this is an object, I could'nt make it work, so I clone the entire object (this.objet = _.cloneDeep(this.object) in the parent component so then the child component detects that changes happened.

Is there any other way of doing this that does not clone the entire object? Thanks in advance

EDIT:

Child Component

export class ChildComponent implements OnInit, OnChanges {
  @Input() public object: any;
}

html

<div>
   <span>{{object.attribute}}</span>
   <div *ngFor="let items of object.attribute2">{{item.value}}</div>
</div>

Parent Component

export class ParentComponent implements OnInit {
  public object: any;

  updateObject() {
      this.object.attribute2[1] = 'Changed value';
      this.object = _.cloneDeep(this.object);
  }
}

html

<div>
   <child-component [object]="object"></child-component>
</div>
Tony Ngo
  • 19,166
  • 4
  • 38
  • 60
mordecai
  • 529
  • 5
  • 25
  • Can you show your template? How you are using? – Prashanth Damam Sep 06 '19 at 11:04
  • @legin use this for getting value on change `@Input() set Inputname(value: number) { console.log(value); }` – Abhishek Sep 06 '19 at 11:20
  • The code snippets you have provided aren't really usable. e.g. your example object has an array of objects with no valid syntax, while your `updateObject`-function replaces the objects with strings. And your `ngfor` creates a variable called `items`, but you attempt to read the value of `item` - and if you replaced the object containing the value with a string, as a primitive type that won't have a `value`-property either. Please provide a minimal, reproducable example that showcases the error on e.g. StackBlitz. – CGundlach Sep 06 '19 at 11:46
  • Possible duplicate of [How to detect when an @Input() value changes in Angular?](https://stackoverflow.com/questions/38571812/how-to-detect-when-an-input-value-changes-in-angular) – Christopher Peisert Sep 06 '19 at 11:57

2 Answers2

1

An efficient way is to use EventEmitter and service communication to trigger changes in the child component.

On way as mentioned by @Tony is to use ngOnChanges(). It is a good shortcut for detecting bounded properties change but as you add more and more bindings, using this hook will affect you application in the long run because it will run every time any of the bound property changes whether or not you desire it all the calls.

So for Service based communication, I've created an example on

In this example, I am binding an Array to the child component using @Input() an on addition of new data, the array is updated by the parent and the latest value is passed on the service which then emits this value. The child component subscribes to this value and the relevant code is executed.

The Service:

import { Injectable, EventEmitter } from '@angular/core';

@Injectable({
    providedIn: "root"
})
export class DataService {

  dataUpdated:EventEmitter<any> = new EventEmitter();

  constructor() { }

  setLatestData(data) {
    this.dataUpdated.emit(data);
  }

}

Child Component TS

import { Component, OnInit, Input } from '@angular/core';
import { DataService } from '../data-service.service';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {

  @Input() allData: [];
  latestData: any;

  constructor(private dataService: DataService) { }

  ngOnInit() {
    this.dataService.dataUpdated.subscribe((data) => {
      this.latestData = data;
    });
  }

}

Child Component HTML

<p>
Latest Data: {{ latestData }}
</p>
<h3>List:</h3>
<li *ngFor="let data of allData">
  {{ data }}
</li>

Parent Component TS

import { Component } from '@angular/core';
import { DataService } from './data-service.service'

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular';
  dataArr = [];

  constructor(private dataService: DataService){}

  onAddTimestamp() {
    let timestamp = new Date();
    this.dataArr.push(timestamp);
    this.dataService.setLatestData(timestamp);
  }

}

Parent Component HTML

<hello name="{{ name }}"></hello>
<p>
  Start editing to see some magic happen :)
</p>
<button
(click)="onAddTimestamp()"
>
  Add Timestamp
</button>
<app-child
[allData] = "dataArr"
></app-child>
Yash
  • 3,438
  • 2
  • 17
  • 33
0

Use the ngOnChanges() lifecycle method in your component.

ngOnChanges is called right after the data-bound properties have been checked and before view and content children are checked if at least one of them has changed.

Some like this

@Input() object: string;

ngOnChanges(changes: SimpleChanges) {
    console.log(changes.object.currentValue);
    // You can also use object.previousValue and 
    // object.firstChange for comparing old and new values
}
Tony Ngo
  • 19,166
  • 4
  • 38
  • 60