Meine Komponente verfügt über Stile, die von der aktuellen Datums- und Uhrzeitangabe abhängen. In meiner Komponente habe ich die folgende Funktion.
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
wird aus der aktuellen datetime berechnet. Die Farbe ändert dtoDate
sich in den letzten 24 Stunden.
Der genaue Fehler ist der folgende:
Der Ausdruck hat sich geändert, nachdem er überprüft wurde. Vorheriger Wert: 'hsl (123, 80%, 49%)'. Aktueller Wert: 'hsl (123, 80%, 48%)'
Ich weiß, dass die Ausnahme im Entwicklungsmodus nur zum Zeitpunkt der Überprüfung des Werts angezeigt wird. Wenn sich der überprüfte Wert vom aktualisierten Wert unterscheidet, wird die Ausnahme ausgelöst.
Daher habe ich versucht, die aktuelle Datums- und Uhrzeitangabe in jedem Lebenszyklus mithilfe der folgenden Hook-Methode zu aktualisieren, um die Ausnahme zu vermeiden:
ngAfterViewChecked()
{
console.log( "! changement de la date du composant !" );
this.dateNow = new Date();
}
...aber ohne Erfolg.
quelle
Antworten:
Führen Sie die Änderungserkennung explizit nach der Änderung aus:
quelle
ngOnInit()
ist dies normalerweise der erste Ort. Wenn der Code vom gerenderten DOM abhängtngAfterViewInit()
oderngAfterContentInit()
die nächsten Optionen sind.ngOnChanges()
ist eine gute Anpassung, wenn der Code jedes Mal ausgeführt werden soll, wenn eine Eingabe geändert wurde.ngDoCheck()
dient zur benutzerdefinierten Änderungserkennung. Eigentlich weiß ich nicht, wofürngAfterViewChecked()
am besten verwendet wird. Ich denke, es heißt kurz davor oder danachngAfterViewInit()
.clientWidth
usw. basiert .TL; DR
Obwohl dies eine Problemumgehung ist, ist es manchmal sehr schwierig, dieses Problem besser zu lösen. Machen Sie sich also keine Vorwürfe, wenn Sie diesen Ansatz verwenden. Das ist okay.
Beispiele : Das ursprüngliche Problem [ Link ], gelöst mit
setTimeout()
[ Link ]Wie vermeide ich?
Im Allgemeinen tritt dieser Fehler normalerweise nach dem Hinzufügen auf (auch in übergeordneten / untergeordneten Komponenten)
ngAfterViewInit
. Die erste Frage ist also, sich zu fragen: Kann ich ohne lebenngAfterViewInit
? Vielleicht verschieben Sie den Code irgendwohin (ngAfterViewChecked
könnte eine Alternative sein).Beispiel : [ Link ]
Ebenfalls
Auch asynchrone Inhalte
ngAfterViewInit
, die sich auf DOM auswirken, können dies verursachen. Kann auch übersetTimeout
oder durch Hinzufügen desdelay(0)
Operators in der Pipe gelöst werden :Beispiel : [ Link ]
Schöne Lektüre
Guter Artikel darüber, wie man das debuggt und warum es passiert: Link
quelle
Wie von @leocaseiro zum Thema Github erwähnt .
quelle
Sie gehen zwei Lösungen!
1. Ändern Sie ChangeDetectionStrategy in OnPush
Für diese Lösung sagen Sie im Grunde eckig:
Die schnelle Lösung:
Ändern Sie Ihre Komponente so, dass sie verwendet wird
ChangeDetectionStrategy.OnPush
Damit scheinen die Dinge nicht mehr zu funktionieren. Das liegt daran, dass Sie Angular von nun an
detectChanges()
manuell aufrufen müssen.Hier ist ein Link, der mir geholfen hat, ChangeDetectionStrategy richtig zu verstehen : https://alligator.io/angular/change-detection-strategy/
2. Grundlegendes zu ExpressionChangedAfterItHasBeenCheckedError
Hier ist ein kleiner Auszug aus der Antwort von tomonari_t über die Ursachen für diesen Fehler. Ich habe versucht, nur die Teile einzubeziehen , die mir geholfen haben, dies zu verstehen.
Der vollständige Artikel zeigt echte Codebeispiele zu jedem hier gezeigten Punkt.
Die Hauptursache ist der Winkellebenszyklus:
Die folgenden Vorgänge werden bei Digest-Zyklen überprüft:
Und so wird der Fehler ausgelöst wird, wenn die verglichenen Werte unterschiedlich sind. , erklärte Blogger Max Koretskyi :
Und schließlich sind hier einige Beispiele aus der Praxis, die normalerweise diesen Fehler verursachen:
Jedes Beispiel finden Sie hier (plunkr). In meinem Fall war das Problem eine dynamische Komponenteninstanziierung.
Aus eigener Erfahrung empfehle ich jedem dringend, die
setTimeout
Lösung zu vermeiden . In meinem Fall kam es zu einer "fast" Endlosschleife (21 Anrufe, die ich Ihnen nicht zeigen möchte, wie man sie provoziert).Ich würde empfehlen, immer die Winkellebenszyklen im Auge zu behalten, damit Sie berücksichtigen können, wie sie sich jedes Mal auswirken, wenn Sie den Wert einer anderen Komponente ändern. Mit diesem Fehler sagt Ihnen Angular:
Der gleiche Blog sagt auch:
Eine kurze Anleitung für mich ist, beim Codieren mindestens die folgenden zwei Dinge zu berücksichtigen ( ich werde versuchen, sie im Laufe der Zeit zu ergänzen ):
@Input
und@Output
Direktiven verwenden, versuchen Sie zu vermeiden, dass Lyfecycle-Änderungen ausgelöst werden, es sei denn, die Komponente ist vollständig initialisiert.this.cdr.detectChanges();
diese mehr Fehler auslösen können, insbesondere wenn Sie mit vielen dynamischen Daten arbeitenthis.cdr.detectChanges();
obligatorisch ist, stellen Sie sicher, dass die verwendeten Variablen (@Input, @Output, etc
) am rechten Erkennungshaken (OnInit, OnChanges, AfterView, etc
) gefüllt / initialisiert sind.Ebenfalls
Wenn Sie Angular Life Hook vollständig verstehen möchten, empfehle ich Ihnen, die offizielle Dokumentation hier zu lesen:
quelle
ChangeDetectionStrategy
umOnPush
es für mich zu reparieren. Ich habe eine einfache Komponente, die hat[disabled]="isLastPage()"
. Diese Methode liest dasMatPaginator
-ViewChild und kehrt zurückthis.paginator !== undefined ? this.paginator.pageIndex === this.paginator.getNumberOfPages() - 1 : true;
. Der Paginator ist nicht sofort verfügbar, aber nachdem er mit gebunden wurde@ViewChild
. Durch Ändern desChangeDetectionStrategy
wurde der Fehler behoben - und die Funktionalität ist nach wie vor vorhanden. Ich bin mir nicht sicher, welche Nachteile ich jetzt habe, aber danke!OnPush
besteht darin, dass Sie siethis.cdr.detectChanges()
jedes Mal verwenden müssen, wenn Sie möchten, dass Ihre Komponente aktualisiert wird. Ich denke du hast es schon benutztIn unserem Fall haben wir das Problem behoben, indem wir changeDetection in die Komponente eingefügt und DetectChanges () in ngAfterContentChecked wie folgt aufgerufen haben
quelle
Ich denke, die beste und sauberste Lösung, die Sie sich vorstellen können, ist folgende:
Getestet mit Angular 5.2.9
quelle
Eine kleine Arbeit, die ich oft benutzt habe
Ich ersetze hauptsächlich "null" durch eine Variable, die ich im Controller verwende.
quelle
Obwohl es bereits viele Antworten und einen Link zu einem sehr guten Artikel zur Änderungserkennung gibt, wollte ich hier meine zwei Cent geben. Ich denke, die Überprüfung ist aus einem bestimmten Grund vorhanden. Daher habe ich über die Architektur meiner App nachgedacht und festgestellt, dass die Änderungen in der Ansicht mithilfe
BehaviourSubject
des richtigen Lebenszyklus-Hooks behoben werden können. Also hier ist, was ich für eine Lösung getan habe.Daher habe ich die zugrunde liegende JavaScript-Klasse erhalten und muss meinen eigenen Kalenderheader für die Komponente initialisieren. Das erfordert
ViewChild
, dass das gerendert wird, bevor mein Elternteil gerendert wird, was Angular nicht funktioniert. Aus diesem Grund habe ich den Wert, den ich für meine Vorlage benötige, in Folgendes verpacktBehaviourSubject<View>(null)
:Wenn ich sicher sein kann, dass die Ansicht aktiviert ist, aktualisiere ich diesen Betreff mit dem Wert aus
@ViewChild
:Dann verwende ich in meiner Vorlage einfach die
async
Pipe. Kein Hacken mit Änderungserkennung, keine Fehler, funktioniert reibungslos.Bitte zögern Sie nicht zu fragen, ob Sie weitere Details benötigen.
quelle
Verwenden Sie einen Standardformularwert, um den Fehler zu vermeiden.
Anstatt die akzeptierte Antwort der Anwendung von detectChanges () in ngAfterViewInit () zu verwenden (wodurch auch der Fehler in meinem Fall behoben wurde), habe ich stattdessen beschlossen, einen Standardwert für ein dynamisch erforderliches Formularfeld zu speichern, damit bei einer späteren Aktualisierung des Formulars Die Gültigkeit wird nicht geändert, wenn der Benutzer eine Option im Formular ändert, die die neuen erforderlichen Felder auslöst (und die Schaltfläche zum Senden deaktiviert).
Dadurch wurde ein kleines Stück Code in meiner Komponente gespeichert, und in meinem Fall wurde der Fehler vollständig vermieden.
quelle
this.cdr.detectChanges()
Ich habe diesen Fehler erhalten, weil ich eine Variable deklariert habe und später
ihren Wert mit ändern wollte
ngAfterViewInit
um das zu beheben, wechselte ich von
zu
quelle