skip to Main Content

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


  1. 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.

    Login or Signup to reply.
  2. Your ratingCount is updated from an observable so you have a couple options:

    1. If you get your result as an observable you could use the async pipe in html:[ratingCountData]="ratingCounts | async"
    2. You can run this.cdr.detectChanges(); after you update it to trigger change detection manually
    3. In case your subscription is outside the Angular zone, run the update in a zone this.zone.run(() => { this.ratingCounts = this.getRatingCount(); });
    4. If you’re using something like rxjs, you could have ratingCountData as an Subject and push the new changes to it(with .next()) when they are available. You would then use the async pipe to subscribe to it in html.
    Login or Signup to reply.
  3. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search