I have a two dimensional array in my parent component and I’m trying to pass the inner arrays to the child component but it doesn’t trigger the Angular change detection so it always shows an empty array.
Here is a shortened version of the .ts code of the parent component:
export class AssessmentResultComponent implements OnInit {
assessmentResult: Rating[] = [];
ratingCounts: number[][] = this.getRatingCount();
constructor(private readonly assessmentResultService: AssessmentResultService, private readonly errorHandlingService: ErrorHandlingService, private readonly route: ActivatedRoute) {
}
ngOnInit()
:
void {
this.assessmentResultService.getAssessmentResults(Number(this.route.snapshot.paramMap.get('id')))
.subscribe({
next: (response) => {
this.assessmentResult = response;
this.ratingCounts = this.getRatingCount();
console.log(this.ratingCounts)
},
error: (error) => this.errorHandlingService.handleError(error),
});
}
}
Here is an example line of the HTML of the parent component and how I pass the inner array:
<app-bar-graph-multiple-choice [ratingCountData]="ratingCounts[0]"></app-bar-graph-multiple-choice>
And here is the child component .ts and how I want to use the data:
export class BarGraphMultipleChoiceComponent implements OnChanges{
/*this doesn't update*/
@Input() ratingCountData: number[] = [];
chartOptions = {
data: [{
type: "bar",
dataPoints: [
{ label: this.label1, y: this.ratingCountData[0] },
{ label: this.label2, y: this.ratingCountData[1] },
{ label: this.label3, y: this.ratingCountData[2] },
{ label: this.label4, y: this.ratingCountData[3] },
{ label: this.label5, y: this.ratingCountData[4] }
]
}]
};
ngOnChanges(changes: SimpleChanges): void {
/*if (changes['ratingCountData'].previousValue !== changes['ratingCountData'].currentValue) {
this.ratingCountData = changes['ratingCountData'].currentValue;
this.chartOptions.data[0].dataPoints[0].y = this.ratingCountData[0];
this.chartOptions.data[0].dataPoints[1].y = this.ratingCountData[1];
this.chartOptions.data[0].dataPoints[2].y = this.ratingCountData[2];
this.chartOptions.data[0].dataPoints[3].y = this.ratingCountData[3];
this.chartOptions.data[0].dataPoints[4].y = this.ratingCountData[4];
}*/
console.log(this.ratingCountData);
}
}
Here is the getRatingCount() Method:
getRatingCount(): number[][] {
let result: number[][] = new Array(5);
for (let i = 0; i < 5; i++) {
result[i] = new Array(5);
}
for (let i = 0; i < result.length; i++) {
for (let j = 0; j < result[i].length; j++) {
result[i][j] = 0;
}
}
this.assessmentResult.forEach(function (currentResult) {
result[0][currentResult.zeitaufwandRating - 1]++;
result[1][currentResult.inhaltRating - 1]++;
result[2][currentResult.stoffmengeRating - 1]++;
result[3][currentResult.niveauRating - 1]++;
result[4][currentResult.relevanzRating - 1]++;
});
return result;
}
I expect that the Angular change detection detects that the array has new content and that it shows on the page accordingly.
3
Answers
Without the code of your getRatingCount method, I can only assume that you add data to the existing instance of your matrix (ratingCounts). We have to keep in mind what the Change Detector in your Angular component is keeping track of: as far as you are working with a non-primitive value, the Change Detector will not be triggered until the reference to your object changes.
In fact, almost all of the Change Detectors in frameworks like Angular keep track of changes that happen in the stack zone of the RAM. Instead of pushing values in the matrix, you have to create the entire matrix from scratch. You can do this on your own, or using an Immutable-like library that, when you do something like (i.e.) a push, a brand new instance is returned.
By the way, if you could share the code placed in the getRatingCount method it would be great for further help.
Your ratingCount is updated from an observable so you have a couple options:
[ratingCountData]="ratingCounts | async"
this.cdr.detectChanges();
after you update it to trigger change detection manuallythis.zone.run(() => { this.ratingCounts = this.getRatingCount(); });
rxjs
, you could have ratingCountData as anSubject
and push the new changes to it(with .next()) when they are available. You would then use theasync
pipe to subscribe to it in html.In Angular, change detection is an important mechanism that checks for changes in the application state and updates the view accordingly. By default, Angular uses a strategy called Zone.js to detect changes.