简体   繁体   中英

How to make child component detects object (@Input()) from parent component has changed in Angular

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>

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>

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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM