Bitte erklären Sie mir, warum ich diesen Fehler immer wieder bekomme: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Natürlich bekomme ich es nur im Dev-Modus, es passiert nicht in meinem Produktions-Build, aber es ist sehr ärgerlich und ich verstehe einfach nicht die Vorteile eines Fehlers in meiner Dev-Umgebung, der nicht auf dem Produkt angezeigt wird - -wahrscheinlich wegen meines Unverständnisses.
Normalerweise ist das Update einfach genug. Ich packe den Fehler verursachenden Code einfach in ein setTimeout wie folgt:
setTimeout(()=> {
this.isLoading = true;
}, 0);
Oder Kraft erfassen Änderungen mit einem Konstruktor wie folgt aus : constructor(private cd: ChangeDetectorRef) {}
:
this.isLoading = true;
this.cd.detectChanges();
Aber warum stoße ich ständig auf diesen Fehler? Ich möchte es verstehen, damit ich diese hackigen Korrekturen in Zukunft vermeiden kann.
quelle
Antworten:
Ich hatte ein ähnliches Problem. Als ich mir die Dokumentation zu Lifecycle Hooks ansah , wechselte ich
ngAfterViewInit
zungAfterContentInit
und es funktionierte.quelle
setTimeout
oben gezeigten hat den Trick getan. Vielen Dank!ngAfterContentChecked
funktioniert hier, währendngAfterContentInit
noch Fehler auslöst.Dieser Fehler weist auf ein echtes Problem in Ihrer Anwendung hin. Daher ist es sinnvoll, eine Ausnahme auszulösen.
Bei der
devMode
Änderungserkennung wird nach jedem regulären Änderungserkennungslauf eine zusätzliche Drehung hinzugefügt, um zu überprüfen, ob sich das Modell geändert hat.Wenn sich das Modell zwischen der regulären und der zusätzlichen Änderungserkennungsrunde geändert hat, bedeutet dies, dass entweder
die beide schlecht sind, weil es nicht klar ist, wie es weitergehen soll, weil sich das Modell möglicherweise nie stabilisiert.
Wenn Angular Runs die Änderungserkennung ausführen, bis sich das Modell stabilisiert hat, wird es möglicherweise für immer ausgeführt. Wenn Angular keine Änderungserkennung ausführt, spiegelt die Ansicht möglicherweise nicht den aktuellen Status des Modells wider.
Siehe auch Was ist der Unterschied zwischen Produktions- und Entwicklungsmodus in Angular2?
quelle
ngOnInit
oderngOnChanges
zum Ändern des Modells verursacht (einige Lebenszyklus-Rückrufe ermöglichen das Ändern des Modells, andere nicht, ich erinnere mich nicht genau, welche dies tun oder nicht tun). Binden Sie nicht an Methoden oder Funktionen in der Ansicht, sondern binden Sie an Felder und aktualisieren Sie die Felder in Ereignishandlern. Wenn Sie an Methoden binden müssen, stellen Sie sicher, dass diese immer dieselbe Wertinstanz zurückgeben, solange keine Änderung vorgenommen wurde. Die Änderungserkennung ruft diese Methoden häufig auf.changeRef.detectChanges()
eine Lösung ist / den Fehler unterdrückt, ist ein Beweis dafür. Es ist wie das Ändern des Zustands$scope.$watch()
in Angular 1.cdRef.detectChanges()
nur in einigen seltsamen Randfällen erforderlich ist, und Sie sollten genau hinschauen, wenn Sie es brauchen, damit Sie richtig verstehen, warum.Als ich die Angular Lifecycle Hooks und ihre Beziehung zur Änderungserkennung verstand, kam viel Verständnis auf .
Ich habe versucht, Angular dazu zu bringen, ein globales Flag zu aktualisieren, das
*ngIf
an das eines Elements gebunden ist, und ich habe versucht, dieses Flag innerhalb desngOnInit()
Lebenszyklus-Hooks einer anderen Komponente zu ändern .Laut Dokumentation wird diese Methode aufgerufen, nachdem Angular bereits Änderungen erkannt hat:
Das Aktualisieren des Flags innerhalb von
ngOnChanges()
wird also keine Änderungserkennung auslösen. Sobald die Änderungserkennung natürlich wieder ausgelöst wurde, hat sich der Wert des Flags geändert und der Fehler wird ausgelöst.In meinem Fall habe ich Folgendes geändert:
Dazu:
und es hat das Problem behoben :)
quelle
router.navigate
beim Laden zu einem Fragment zu scrollen, wenn es in der URL vorhanden ist. Dieser Code wurde ursprünglich an der Stelle platziert,AfterViewInit
an der ich den Fehler erhalten habe. Dann bin ich umgezogen, wie Sie dem Konstruktor gesagt haben, aber das Fragment wurde nicht berücksichtigt. Umzug inngOnInit
gelöst :) danke!setInterval()
funktioniert auch, wenn es nach einem anderen lebenslangen Ereigniscode ausgelöst werden muss.Aktualisieren
Ich empfehle dringend, zuerst mit der Selbstantwort des OP zu beginnen : Überlegen Sie genau, was im
constructor
Vergleich zu dem, was im OP getan werden soll, getan werden kannngOnChanges()
.Original
Dies ist eher eine Randnotiz als eine Antwort, aber es könnte jemandem helfen. Ich bin auf dieses Problem gestoßen, als ich versucht habe, das Vorhandensein einer Schaltfläche vom Status des Formulars abhängig zu machen:
Soweit ich weiß, führt diese Syntax dazu, dass die Schaltfläche basierend auf der Bedingung zum DOM hinzugefügt und daraus entfernt wird. Was wiederum zum führt
ExpressionChangedAfterItHasBeenCheckedError
.Die Lösung in meinem Fall (obwohl ich nicht behaupte, die vollständigen Auswirkungen des Unterschieds zu erfassen) bestand darin,
display: none
stattdessen Folgendes zu verwenden :quelle
[hidden]
anstelle des sehr ausführlichen[style.display]
Teils verwenden. :)[hidden]
nicht immer das gleiche Verhalten wiedisplay: none
Es gab interessante Antworten, aber ich schien keine zu finden, die meinen Bedürfnissen entsprach. Die nächste stammt von @ chittrang-mishra, die sich nur auf eine bestimmte Funktion bezieht und nicht auf mehrere Schalter wie in meiner App.
Ich wollte nicht
[hidden]
ausnutzen,*ngIf
nicht einmal Teil des DOM zu sein, also fand ich die folgende Lösung, die möglicherweise nicht für alle die beste ist, da sie den Fehler unterdrückt, anstatt ihn zu korrigieren, aber in meinem Fall, wo ich das kenne Das Endergebnis ist korrekt, es scheint für meine App in Ordnung zu sein.Was ich getan habe, war implementieren
AfterViewChecked
, hinzufügenconstructor(private changeDetector : ChangeDetectorRef ) {}
und dannIch hoffe, das hilft anderen, da mir viele andere geholfen haben.
quelle
Angular-Läufe ändern die Erkennung und wenn festgestellt wird, dass einige Werte, die an die untergeordnete Komponente übergeben wurden, geändert wurden, löst Angular den Fehler aus:
ExpressionChangedAfterItHasBeenCheckedError
Klicken Sie für mehrUm dies zu korrigieren, können wir den
AfterContentChecked
Life Cycle Hook und verwendenquelle
In meinem Fall hatte ich dieses Problem in meiner Spezifikationsdatei, während ich meine Tests ausführte.
Ich musste Änderung
ngIf
zu[hidden]
zu
quelle
[hidden]
: talkdotnet.com/dont-use-hidden-attribute-angularjs-2*ngIf
das DOM geändert wird, indem das Element zur Seite hinzugefügt und daraus entfernt wird, während[hidden]
die Sichtbarkeit des Elements geändert wird, ohne dass es aus dem DOM entfernt wird.Befolgen Sie die folgenden Schritte:
1. Verwenden Sie 'ChangeDetectorRef', indem Sie es wie folgt aus @ angle / core importieren:
2. Implementieren Sie es wie folgt in constructor ():
3. Fügen Sie Ihrer Funktion die folgende Methode hinzu, die Sie bei einem Ereignis wie einem Klick auf die Schaltfläche aufrufen. So sieht es aus:
quelle
Ich habe ng2-Karussellamos (Angular 8 & Bootstrap 4) verwendet.
Unten wurde mein Problem behoben:
Was ich getan habe:
quelle
Ich hatte das gleiche Problem, als sich der Wert in einem der Arrays in meiner Komponente änderte. Aber anstatt die Änderungen bei Wertänderungen zu erkennen, habe ich die Strategie zur Erkennung von Komponentenänderungen in geändert
onPush
(wodurch Änderungen bei Objektänderungen und nicht bei Wertänderungen erkannt werden).quelle
Beziehen Sie sich auf den Artikel https://blog.angularindepth.com/alles, was Sie brauchen, um zu wissen, dass der Ausdruck geändert wurde, nachdem Sie den Fehler- Fehler- e3fd9ce7dbb4 überprüft haben
Die Mechanik hinter der Änderungserkennung funktioniert also tatsächlich so, dass sowohl die Änderungserkennung als auch die Überprüfungsübersicht synchron ausgeführt werden. Das heißt, wenn wir Eigenschaften asynchron aktualisieren, werden die Werte nicht aktualisiert, wenn die Überprüfungsschleife ausgeführt wird, und es wird kein
ExpressionChanged...
Fehler angezeigt. Der Grund für diesen Fehler ist, dass Angular während des Überprüfungsprozesses andere Werte sieht als während der Änderungserkennungsphase. Um das zu vermeiden ...1) Verwenden Sie changeDetectorRef
2) benutze setTimeOut. Dadurch wird Ihr Code in einer anderen VM als Makroaufgabe ausgeführt. Angular sieht diese Änderungen während des Überprüfungsprozesses nicht und Sie erhalten diesen Fehler nicht.
3) Wenn Sie Ihren Code wirklich auf derselben VM ausführen möchten, verwenden Sie wie
Dadurch wird eine Mikroaufgabe erstellt. Die Micro-Task-Warteschlange wird verarbeitet, nachdem der aktuelle Synchroncode ausgeführt wurde. Daher erfolgt die Aktualisierung der Eigenschaft nach dem Überprüfungsschritt.
quelle
@HostBinding
kann eine verwirrende Quelle für diesen Fehler sein.Angenommen, Sie haben die folgende Hostbindung in einer Komponente
Nehmen wir zur Vereinfachung an, diese Eigenschaft wird über die folgende Eingabeeigenschaft aktualisiert:
In der übergeordneten Komponente setzen Sie sie programmgesteuert ein
ngAfterViewInit
Folgendes passiert:
carousel
(über ViewChild).carousel
bisngAfterViewInit()
(es wird null sein)style_groupBG = 'red'
background: red
die Host-ImageCarousel-Komponente eincarousel.style.background
und ist nicht klug genug, um zu wissen, dass dies kein Problem ist, sodass die Ausnahme ausgelöst wird .Eine Lösung besteht darin, einen anderen Wrapper-Div-Insider ImageCarousel einzuführen und die Hintergrundfarbe darauf festzulegen. Dann erhalten Sie jedoch nicht die Vorteile der Verwendung
HostBinding
(z. B. die Möglichkeit für das übergeordnete Element, die vollen Grenzen des Objekts zu steuern).Die bessere Lösung in der übergeordneten Komponente besteht darin, nach dem Festlegen der Konfiguration detectChanges () hinzuzufügen.
Dies mag so offensichtlich aussehen und anderen Antworten sehr ähnlich sein, aber es gibt einen subtilen Unterschied.
Betrachten Sie den Fall, in dem Sie
@HostBinding
erst später während der Entwicklung hinzufügen . Plötzlich erhalten Sie diesen Fehler und es scheint keinen Sinn zu ergeben.quelle
Hier sind meine Gedanken darüber, was passiert. Ich habe die Dokumentation nicht gelesen, bin mir aber sicher, dass dies ein Grund dafür ist, warum der Fehler angezeigt wird.
Bei Verwendung von * ngIf wird das DOM physisch geändert, indem das Element jedes Mal hinzugefügt oder entfernt wird, wenn sich die Bedingung ändert. Wenn sich die Bedingung ändert, bevor sie in die Ansicht gerendert wird (was in Angulars Welt sehr gut möglich ist), wird der Fehler ausgelöst. Siehe Erklärung hier zwischen Entwicklungs- und Produktionsmodus.
Bei der Verwendung
[hidden]
ändert sich das nicht physisch,DOM
sondern es wird lediglichelement
vor der Ansicht verborgen , höchstwahrscheinlichCSS
im Hintergrund. Das Element befindet sich noch im DOM, ist jedoch je nach Wert der Bedingung nicht sichtbar. Aus diesem Grund tritt der Fehler bei der Verwendung nicht auf[hidden]
.quelle
isProcessing()
das tun, müssen Sie!isProcessing()
für die[hidden]
hidden
"verwendet kein CSS im Hintergrund", sondern eine reguläre HTML-Eigenschaft. developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/…Für mein Problem las ich github - "ExpressionChangedAfterItHasBeenCheckedError beim Ändern eines Komponenten-Nicht-Modell-Werts in afterViewInit" und beschloss, das ngModel hinzuzufügen
Es hat mein Problem behoben, ich hoffe es hilft jemandem.
quelle
ngModel
. Und könnten Sie bitte erläutern, warum dies hilfreich sein sollte?Debugging-Tipps
Dieser Fehler kann ziemlich verwirrend sein, und es ist leicht, eine falsche Annahme darüber zu treffen, wann genau er auftritt. Ich finde es hilfreich, viele Debugging-Anweisungen wie diese in den betroffenen Komponenten an den entsprechenden Stellen hinzuzufügen. Dies hilft, den Fluss zu verstehen.
In den übergeordneten put-Anweisungen wie diesen (die genaue Zeichenfolge 'EXPRESSIONCHANGED' ist wichtig), aber ansonsten sind dies nur Beispiele:
In den Rückrufen für Kinder / Dienste / Timer:
Wenn Sie
detectChanges
manuell ausführen, fügen Sie auch dafür die Protokollierung hinzu:Dann filtern Sie im Chrome-Debugger einfach nach 'EXPRESSIONCHANGES'. Dies zeigt Ihnen genau den Ablauf und die Reihenfolge von allem, was eingestellt wird, und auch genau, an welchem Punkt Angular den Fehler auslöst.
Sie können auch auf die grauen Links klicken, um Haltepunkte einzufügen.
Eine andere Sache, die Sie beachten sollten, wenn Sie in Ihrer gesamten Anwendung ähnlich benannte Eigenschaften haben (z. B.
style.background
), stellen Sie sicher, dass Sie diejenige debuggen, von der Sie glauben, dass Sie sie haben - indem Sie sie auf einen unklaren Farbwert setzen.quelle
In meinem Fall hatte ich eine asynchrone Eigenschaft
LoadingService
mit einem BehavioralSubjectisLoading
Die Verwendung des [versteckten] Modells funktioniert, aber * ngIf schlägt fehl
quelle
Eine Lösung, die bei mir mit rxjs funktioniert hat
quelle
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
wenn Wertänderungen ausgelöst werden, wenn sich das DOM ändertdelay
machen den Fehler verschwinden. Es funktioniert ähnlich wiesetTimeout
.Ich hatte diese Art von Fehler in Ionic3 (das Angular 4 als Teil seines Technologie-Stacks verwendet).
Für mich war es das:
<ion-icon [name]="getFavIconName()"></ion-icon>
Also habe ich versucht, den Typ eines Ionensymbols bedingt von a
pin
auf a zu ändernremove-circle
, je nachdem, in welchem Modus ein Bildschirm arbeitet.Ich schätze, ich muss
*ngIf
stattdessen eine hinzufügen .quelle
Mein Problem war offensichtlich, als ich hinzufügte,
*ngIf
aber das war nicht die Ursache. Der Fehler wurde verursacht, indem das Modell in{{}}
Tags geändert wurde und*ngIf
später versucht wurde, das geänderte Modell in der Anweisung anzuzeigen . Hier ist ein Beispiel:Um das Problem zu beheben, habe ich mich
changeMyModelValue()
an einen Ort gewandt, der sinnvoller war.In meiner Situation wollte ich
changeMyModelValue()
immer dann aufgerufen werden, wenn eine untergeordnete Komponente die Daten änderte. Dazu musste ich ein Ereignis in der untergeordneten Komponente erstellen und ausgeben, damit das übergeordnete Element damit umgehen kann (durch AufrufenchangeMyModelValue()
. Siehe https://angular.io/guide/component-interaction#parent-listens-for-child-eventquelle
Ich hoffe, dies hilft jemandem, der hierher kommt: Wir führen Serviceabrufe auf
ngOnInit
folgende Weise durch und verwenden eine VariabledisplayMain
, um die Montage der Elemente im DOM zu steuern.component.ts
und component.html
quelle
Ich habe diesen Fehler erhalten, weil ich eine Variable in component.html verwendet habe, die in component.ts nicht deklariert wurde. Nachdem ich das Teil in HTML entfernt hatte, war dieser Fehler behoben.
quelle
Ich habe diesen Fehler erhalten, weil ich Redux-Aktionen in Modal versendet habe und Modal zu diesem Zeitpunkt nicht geöffnet war. Ich habe Aktionen ausgelöst, sobald die modale Komponente Eingaben erhalten hat. Also habe ich dort setTimeout platziert, um sicherzustellen, dass Modal geöffnet ist und dann die Aktionen gelöscht werden.
quelle
Für alle, die damit zu kämpfen haben. Hier ist eine Möglichkeit, diesen Fehler ordnungsgemäß zu debuggen: https://blog.angular-university.io/angular-debugging/
In meinem Fall habe ich diesen Fehler tatsächlich mit diesem [versteckten] Hack anstelle von * ngIf ... beseitigt.
Aber der Link, den ich bereitgestellt habe, hat es mir ermöglicht, THE GUILTY * ngIf zu finden :)
Genießen.
quelle
hidden
stattngIf
ist kein Hack, und es geht auch nicht auf den Kern des Problems ein. Sie maskieren nur das Problem.Die Lösung ... Services und RXJS ... Event-Emitter und Eigenschaftsbindung verwenden beide RXJs. Sie können sie besser selbst implementieren, haben mehr Kontrolle und sind einfacher zu debuggen. Denken Sie daran, dass Ereignisemitter rxjs verwenden. Erstellen Sie einfach einen Service und lassen Sie jede Komponente innerhalb eines Observable den Beobachter abonnieren und übergeben Sie je nach Bedarf entweder einen neuen Wert oder einen Cosume-Wert
quelle