I need to do next one - get data from backend and forward it to child component:
...
@Component({
selector: 'my-component',
template: `<my-child-component [data]="data"></my-child-component>`
})
public data = [];
ngOnInit() {
this.service.getParentData().subscribe(res => {
res.map(r => this.service.getChildDatas(r.id)
.subscribe(child => this.data.push(child)));
});
}
But in a child component, when I try to print data in ngOnChanges, I see just strange array, I can see elements, but length is equal to 0:
How does it work and how can I achieve my objective (pass data to child and work with data as with array)?
CodePudding user response:
You seems to have 2 observables that you want to pipe, the one after the other. You'll need to use rxJS operators to achieve that.
The way to achieve it is to use operator switchMap, or mergeMap depending if you want the changes of the first to re-trigger the second one.
Then use forkJoin operator, to subscribe to a list of observable and threat results as a list
Assuming that you receive an array first, and need for each array value to get the child one through another service you'll have
ngOnInit() {
this.service.getParentData()
.pipe(switchMap((parentData: {id: string}[]) => {
return forkJoin(parentData.map(r =>
this.service.getChildDatas(r.id))
})
.subscribe((childData: any[]) => {this.data = childData});
}
CodePudding user response:
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
ngOnInit(){
this.service.getParentData().subscribe(res => {
res.map(r => this.service.getChildDatas(r.id)
.subscribe(child => {
this.data.push(child);
this.data = [...this.data]; // It will re assign new instance
data(Array) on every push
}));
});
}
But I will suggest you to optimise your code little bit here like
ngOnInit(){
this.service.getParentData().pipe(
concatMap((res) => this.service.getChildDatas(res.id))
).subscribe({
next: (res) => {
this.data.push(res);
this.data = [...this.data];
},
});
}
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.
<my-child-component [data]="{trackBy:data.length,data:data}"></my-child-component>
ngOnChanges(changes: SimpleChanges): void {
console.log(changes.data.currentValue.data);
}
Update:
You used .push
,so I assumed that You are receiving a single data,But If you are receiving an array then you can assign res array to this.data
,by doing this you are changing reference everytime
ngOnInit(){
this.service.getParentData().pipe(
concatMap((res) => this.service.getChildDatas(res.id))
).subscribe({
next: (res) => {
this.data = res;
},
});
}
One more Update
Use ngOnChanges
when you are likely dealing with multiple properties changing at once, or wish to do something with multiple properties.
But if you want to handle specific property changes,then better option is to use setter alongside @Input
.
@Input() set data(data:any[]){
console.log(data);
};
I found this blog very helpful Detecting @Input changes in Angular with ngOnChanges and Setters