Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid the ExpressionChanged error, when using a method to set [ngClass]?

Tags:

angular

I have the following components:

client-card.component.html:

<div id="content">
<div id="card" (click)="viewDetails(client.clientId)">
  <div id="front">
    <div id="top-pic" [ngClass]="setTopPic()"></div>
    <div id="avatar"></div>
    <div id="info-box" [class.green]="(i + 1) % 2 == 0" [class.blue]="(i + 1) % 2 != 0">
      <div class="info">
        <h1>{{ client.surname }}, {{ client.forename }}</h1>
        <h2>{{ client.postcode }}</h2>
      </div>
    </div>
  </div>
</div>

client-card.component.ts:

import { Router } from '@angular/router';
import { Component, Input, OnInit } from '@angular/core';

@Component({
  selector: 'app-client-card',
  templateUrl: './client-card.component.html',
  styleUrls: ['./client-card.component.css']
})
export class ClientCardComponent implements OnInit {

  @Input() client: any = {};
  @Input() i: number;

  constructor(private router: Router) { }

  ngOnInit() {
    console.log(`Init Card Component`)
  }

  viewDetails(clientId: number) {
    this.router.navigate(['/client', clientId]);
  }

  setTopPic() : string {
    if((this.i + 1) % 2 == 0) {
      return "blue" + Math.ceil(Math.random() * 4).toString();
    } else {
      return "green" + Math.ceil(Math.random() * 4).toString();
    }
  }

}

client-list.component.html:

<div class="row">
  <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3" *ngFor="let client of clients; let i = index">
    <app-client-card [client]="client" [i]="i"></app-client-card>
  </div>
</div>

Whenever the client-list component displays the cards, I receive the following error in the console:

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'blue1'. Current value: 'blue2'.

I've read a few different explanations of various causes of this error, but am unfortunately no closer to resolving it.

Any assistance greatly appreciated.

like image 913
OGarvey Avatar asked Sep 08 '25 13:09

OGarvey


1 Answers

Usually you'll get this error while running in non-production mode and the cause is change detection running two times to check if you're not having a bug somewhere in your code which modifies data outside of the zone.

What's happening here is that your method setTopPic() might return a different value everytime it's called, so change detection is seeing blue1 on checking the binding and blue2 after that.

I'd suggest to store the class name in a member variable on init, since it relies on your other member variable i:

export class ClientCardComponent implements OnInit {

  @Input() client: any = {};
  @Input() i: number;

  topPic: string;

  constructor(private router: Router) { }

  ngOnInit() {
    console.log(`Init Card Component`);
    this.topPic = setTopPic(); // <-- here
  }

And use that in the binding:

<div id="top-pic" [ngClass]="topPic"></div>
like image 104
rinukkusu Avatar answered Sep 10 '25 02:09

rinukkusu