Appearance
NG0100 - Expression Changed After Checked Error
Réponse courte
Angular, en mode développement, effectue deux cycles de détection des changements. Vous essayez d'utiliser une valeur qui a changé depuis la dernière vérification.
En mode développement, Angular effectue deux cycles de détection des changements :
- Le premier cycle vérifie l'état initial
- Le second cycle vérifie qu'aucune valeur n'a changé depuis le premier cycle
Pourquoi C'est Important ?
Prenons un exemple concret :
typescript
Parent (Prix total : 100€)
└── Enfant (Affiche le prix)
Si l'enfant modifie le prix après la vérification :
typescript
Parent (Prix total : 120€) // Changement inattendu !
└── Enfant (Affiche toujours 100€) // Affichage incorrect
INFORMATION
Ce double cycle n'existe qu'en mode développement pour nous aider à détecter les problèmes potentiels. En production, un seul cycle est effectué.
Imaginons une situation de la vie quotidienne : vous êtes caissier dans un supermarché. Après avoir scanné tous les articles d'un client et annoncé le total, le client ajoute soudainement un nouvel article. C'est exactement ce qui se passe avec cette erreur - Angular a "terminé sa caisse" mais découvre que quelque chose a changé après coup !
Exemple Concret
Voici un exemple simple qui provoque cette erreur :
ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>{{ valeurAleatoire }}</h1>
`
})
export class AppComponent {
get valeurAleatoire() {
return Math.random();
}
}
Ce code génère l'erreur suivante :
ERROR RuntimeError: NG0100: ExpressionChangedAfterItHasBeenCheckedError:
Expression has changed after it was checked.
Pourquoi ?
Cette erreur se produit car à chaque fois que valeurAleatoire
est appelée, elle retourne une nouvelle valeur aléatoire. Angular vérifie la valeur une première fois, puis lors de sa vérification supplémentaire (en mode développement), il constate que la valeur a changé !
Comment Résoudre Cette Erreur ?
Voici plusieurs solutions selon votre cas d'usage :
1. Initialisation dans le constructeur ou ngOnInit
ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>{{ valeurAleatoire }}</h1>
`
})
export class AppComponent implements OnInit {
valeurAleatoire!: number;
ngOnInit() {
this.valeurAleatoire = Math.random();
}
}
2. Utilisation des signaux pour les valeurs dynamiques
ts
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>{{ valeurAleatoire() }}</h1>
<button (click)="genererNouvelleValeur()">Nouvelle valeur</button>
`
})
export class AppComponent {
valeurAleatoire = signal(Math.random());
genererNouvelleValeur() {
this.valeurAleatoire.set(Math.random());
}
}
BONNE PRATIQUE
Préférez toujours l'utilisation de valeurs stables dans vos templates. Si une valeur doit changer, assurez-vous que le changement soit déclenché par une action utilisateur ou un événement clairement défini.
Cas Particuliers
Avec ngAfterViewInit
Si vous devez modifier des valeurs après l'initialisation de la vue, utilisez setTimeout
:
ts
import { Component, AfterViewInit } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>{{ titre }}</h1>
`
})
export class AppComponent implements AfterViewInit {
titre = 'Initial';
ngAfterViewInit() {
setTimeout(() => {
this.titre = 'Modifié';
});
}
}
ATTENTION
L'utilisation de setTimeout
doit rester une solution de dernier recours. Il est préférable de revoir l'architecture de votre composant pour éviter ce genre de situation.