Mon composant a des styles qui dépendent de la date/heure actuelle. Dans ma composante, j'ai la fonction suivante.
private fontColor( dto : Dto ) : string {
// date d'exécution du dto
let dtoDate : Date = new Date( dto.LastExecution );
(...)
let color = "hsl( " + hue + ", 80%, " + (maxLigness - lightnessAmp) + "%)";
return color;
}
lightnessAmp
est calculé à partir de la date/heure actuelle. La couleur change si dtoDate
est dans les dernières 24 heures.
L'erreur exacte est la suivante:
L'expression a changé après avoir été vérifiée. Valeur précédente: 'hsl (123, 80%, 49%)'. Valeur actuelle: 'hsl (123, 80%, 48%)'
Je sais que l'exception n'apparaît en mode de développement qu'au moment où la valeur est vérifiée. Si la valeur cochée est différente de la valeur mise à jour, l'exception est levée.
J'ai donc essayé de mettre à jour la date et l'heure en cours à chaque cycle de vie à l'aide de la méthode de raccordement suivante pour éviter l'exception:
ngAfterViewChecked()
{
console.log( "! changement de la date du composant !" );
this.dateNow = new Date();
}
... mais sans succès.
Exécuter la détection de changement explicitement après le changement:
import { ChangeDetectorRef } from '@angular/core';
constructor(private cdRef:ChangeDetectorRef) {}
ngAfterViewChecked()
{
console.log( "! changement de la date du composant !" );
this.dateNow = new Date();
this.cdRef.detectChanges();
}
Ceci est un bon article pour comprendre cette erreur. Ce n'est pas trop long à lire.
Comme mentionné par @leocaseiro sur question github .
J'ai trouvé 3 solutions pour ceux qui recherchent des solutions faciles.
1) Passage de
ngAfterViewInit
àngAfterContentInit
2) Passage à
ngAfterViewChecked
combiné avecChangeDetectorRef
en tant que suggéré sur # 14748 (commentaire)3) Conservez ngOnInit () mais appelez
ChangeDetectorRef.detectChanges()
après vos changements.
Petite solution de contournement pour ce problème:
ngAfterViewInit() { // or ngOnInit or whatever
setTimeout(() => {
this.dateNow = new Date();
});
}
Je pense la solution la meilleure et la plus propre vous pouvez l’imaginer est la suivante:
@Component( {
selector: 'app-my-component',
template: `<p>{{ myData?.anyfield }}</p>`,
styles: [ '' ]
} )
export class MyComponent implements OnInit {
private myData;
constructor( private myService: MyService ) { }
ngOnInit( ) {
/*
async .. await
clears the ExpressionChangedAfterItHasBeenCheckedError exception.
*/
this.myService.myObservable.subscribe(
async (data) => { this.myData = await data }
);
}
}
Testé avec Angular 5.2.9
Dans notre cas, nous FIXONS en ajoutant changeDetection au composant et appelons detectChanges () dans ngAfterContentChecked, codez comme suit
@Component({
selector: 'app-spinner',
templateUrl: './spinner.component.html',
styleUrls: ['./spinner.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SpinnerComponent implements OnInit, OnDestroy, AfterContentChecked {
show = false;
private subscription: Subscription;
constructor(private spinnerService: SpinnerService, private changeDedectionRef: ChangeDetectorRef) { }
ngOnInit() {
this.subscription = this.spinnerService.spinnerState
.subscribe((state: SpinnerState) => {
this.show = state.show;
});
}
ngAfterContentChecked(): void {
this.changeDedectionRef.detectChanges();
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
Un petit travail que j'ai utilisé plusieurs fois
Promise.resolve(null).then(() => {
console.log( "! changement de la date du composant !" );
this.dateNow = new Date();
this.cdRef.detectChanges();
});
Je remplace principalement "null" par une variable que j'utilise dans le contrôleur.
Voici un petit extrait de tomonari_tréponse sur les causes de cette erreur, j'ai essayé d'inclure uniquement les parties qui m'ont aidé à comprendre cela.
L'article complet montre des exemples de code réels sur chaque point présenté ici.
La cause fondamentale est celle du cycle de vie angulaire:
Après chaque opération, Angular se souvient des valeurs qu’elle utilisait auparavant une opération. Ils sont stockés dans la propriété oldValues du vue des composants.
Une fois les vérifications terminées pour tous les composants, Angular démarre puis le cycle de digestion suivant, mais au lieu d’effectuer des opérations, il compare les valeurs actuelles à celles dont il se souvient. le cycle de digestion précédent.
Les opérations suivantes sont celles en cours de vérification lors du cycle de digestion:
vérifiez que les valeurs transmises aux composants enfants sont identiques à celles de les valeurs qui seraient utilisées pour mettre à jour les propriétés de ces composants à présent.
vérifiez que les valeurs utilisées pour mettre à jour les éléments DOM sont identiques à celles de les valeurs qui seraient utilisées pour mettre à jour ces éléments exécutent maintenant le même.
vérifie tous les composants enfants
Et ainsi, l'erreur est renvoyée lorsque les valeurs comparées sont différentes., blogueur Max Koretskyi a déclaré:
Le coupable est toujours le composant enfant ou une directive.
Et enfin, voici quelques exemples du monde réel qui provoquent généralement cette erreur:
Chaque échantillon peut être trouvé ici (plunkr), dans mon cas, le problème était une instanciation de composant dynamique.
De plus, de par ma propre expérience, je recommande vivement à tous d’éviter la solution setTimeout
. Dans mon cas, cela a provoqué une boucle "presque" infinie (21 appels que je ne suis pas disposé à vous montrer comment les provoquer),
Je recommande de toujours garder à l'esprit le cycle de vie angulaire afin que vous puissiez prendre en compte la manière dont ils seraient affectés chaque fois que vous modifiez la valeur d'un autre composant. Avec cette erreur, Angular vous dit:
Vous faites peut-être cela dans le mauvais sens, êtes-vous sûr d'avoir raison?
Le même blog dit aussi:
Souvent, le correctif consiste à utiliser le bon crochet de détection de changement pour créer un composant dynamique.