Reputation: 1219
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
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
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
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
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
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
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