Ambuj Khanna
Ambuj Khanna

Reputation: 1219

Why ngOnChanges() does not trigger when @Input() update the data Angular 8?

parent.component.html

<parent-comp [data]="mydata"> </parent-comp>

parent.component.ts

this.service.abc$
        .takeUntil(this.ngUnsubscribe.asObservable())
        .subscribe((data: myType[]) => {
            this.mydata= data;
        });

child.component.ts

@Input data;

Under Class I have below code

public ngOnChanges() { if (this.data) { console.log(this.data); } }

Now I want whenever I receive latest data in @Input data from Parent Component to child then my ngOnChanges function should trigger and print data in console.

But unfortunately ngOnChanges function does not trigger again. It trigger only once when component initialize

Please let me know if anyone wants more detail on same!

Thanks!

Upvotes: 7

Views: 21006

Answers (6)

Thomas David Kehoe
Thomas David Kehoe

Reputation: 10930

You can also JSON stringify your object in the parent before passing it to child, then JSON parse the string into the original object in the child.

You don't even have to parse the string in the child. Just stringify the object in the parent, pass it through the parent HTML template alongside the object, and don't make an @Input() for the string in the child. ngOnChanges will fire and you can do stuff with the passed object.

I'm not saying this is the best solution, just that it's a solution. Using the spread operator is the solution I use.

Upvotes: 1

Barremian
Barremian

Reputation: 31125

Given the lack of further information, I'd make an informed guess that @Input data is either an array or an object.

According to docs, ngOnChanges is:

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

What it doesn't say however is how the property should be changed. Generally speaking, the hook is only triggered when the reference to the property is changed.

Consider the following eg.

Parent component controller

mydata = [];

updateMyData(value: any) {
  this.mydata.push(value);
}

Parent component template

<app-child [data]="mydata"></app-child>

Child component controller

@Input() data: any;

ngOnChanges(changes: SimpleChanges) {
  console.log(changes);
}

Now you'd expect the ngOnChanges will be triggered every time the updateMyData() function is called in the parent component. But the reference to the variable mydata is never changed. So the hook won't be triggered. There are multiple ways to force the change detector to trigger the hook.

Method 1: Bind the @Input decorator to a setter instead of the variable directly. Discussed in an answer already.

Method 2: Use spread syntax to re-assign the variable in the parent component.

Parent component controller

mydata = [];

updateMyData(value: any) {
  this.mydata = [...this.mydata, value];
}

You could use the same methods for objects as well.

Upvotes: 10

Ambuj Khanna
Ambuj Khanna

Reputation: 1219

Thank you everyone for your quick and effective solutions.

I got solution and it is not exactly but similar to what you guys suggested.

In the Parent Component:

**Earlier I was assigning this way**
`this.mydata= data;`


**But now I am assigning in below way:**
`this.mydata= cloneDeep(data);`

Note : cloneDeep is imported from lodash

Upvotes: 3

Gauri Kesava Kumar
Gauri Kesava Kumar

Reputation: 474

NgOnChanges will only be triggered for an input-bound property change of primitive type. That is because the reference to the data-variable has to be changed in order for the change to be registered, so that you can get it in this life-cycle hook.

So, the possible way you could achieve this is by changing the reference of 'mydata' variable. Like, assigning a new reference to the mydata variable when it is changed, mydata = [...mydata] if it is an array, or mydata = {...mydata} if it is an object from the parent component.

Upvotes: 2

iamaword
iamaword

Reputation: 1509

It could be the data you are passing down. If it doesn't change then the ngOnChanges won't register any changes. Here's an example, you can see if a property changes multiple times then it will only trigger on the first update, but if you recreate the object it changes every time. (see console logs in stackblitz)

https://stackblitz.com/edit/angular-ivy-qmb35h?file=src%2Fapp%2Fapp.component.html

You can do as I did and recreate the object each time to bypass this, or a more hacky way may be to keep a dummy 'count' variable that you pass down as well, and increment it each time you want the child component to register the change.

Upvotes: 1

Minal Shah
Minal Shah

Reputation: 1486

you can use setter and getter methods for @Input in angular. Please refer the below lines for reference:

private _data: any;
  @Input()
  set data(data) {
    this._data = data;
    console.log(this._data);
  };

From setter method only you can call any other method as well and can run any logic you want from there.

Upvotes: 1

Related Questions