ExpressionChangedAfterItHasBeenCheckedError erklärt

308

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.

Kevin LeStarge
quelle

Antworten:

121

Ich hatte ein ähnliches Problem. Als ich mir die Dokumentation zu Lifecycle Hooks ansah , wechselte ich ngAfterViewInitzu ngAfterContentInitund es funktionierte.

nur ich
quelle
@PhilipEnc Mein Problem hing mit einer Änderung zusammen, die durch die DOM-Änderung ausgelöst wurde. Wenn sich das DOM ändern würde, würde das QueryList-Objekt (das von einer @ ContentChildren-Eigenschaft stammt) aktualisiert, und innerhalb der Methode, die das Update aufgerufen hat, wurde die bidirektionale gebundene Eigenschaft geändert. Dies verursachte das Problem, das ich hatte. Das Umschließen dieser Änderung auf die Zwei-Eigenschaft mit dem setTimeoutoben gezeigten hat den Trick getan. Vielen Dank!
kbpontius
1
In meinem Fall hatte ich Code eingefügt, der den Wert des primeng-Grid-Arrays in ngAfterContentInit geändert hat. Ich habe den Code in ngOnInit eingefügt und es hat funktioniert.
Vibhu
ngAfterContentCheckedfunktioniert hier, während ngAfterContentInitnoch Fehler auslöst.
Ashubuntu
ngAfterContentChecked Verwendung, aber Projekt sehr langsam geladen
Ghotekar Rahul
101

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 Änderungserkennung selbst hat eine Änderung verursacht
  • Eine Methode oder ein Getter gibt bei jedem Aufruf einen anderen Wert zurück

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?

Günter Zöchbauer
quelle
4
Wie kann ich diesen Fehler in Zukunft vermeiden? Gibt es eine andere Möglichkeit, über meinen Code nachzudenken, um sicherzustellen, dass ich nicht dieselben Fehler mache?
Kevin LeStarge
25
Normalerweise wird dies durch einige Lebenszyklus-Rückrufe wie ngOnInitoder ngOnChangeszum Ä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.
Günter Zöchbauer
Für alle, die hier ankommen und diesen Fehler mit der ngx-toaster-Bibliothek erhalten, ist hier der Fehlerbericht: github.com/scttcper/ngx-toastr/issues/160
rmcsharry
2
Es ist nicht unbedingt ein Problem mit der App. Die Tatsache, dass das Aufrufen 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.
Kevin Beal
1
Ich kenne Angular 1 nicht so gut, aber die Änderungserkennung in Angular 2 funktioniert ganz anders. Sie haben Recht, dass es nicht unbedingt ein Problem ist, sondern normalerweise cdRef.detectChanges()nur in einigen seltsamen Randfällen erforderlich ist, und Sie sollten genau hinschauen, wenn Sie es brauchen, damit Sie richtig verstehen, warum.
Günter Zöchbauer
83

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 *ngIfan das eines Elements gebunden ist, und ich habe versucht, dieses Flag innerhalb des ngOnInit()Lebenszyklus-Hooks einer anderen Komponente zu ändern .

Laut Dokumentation wird diese Methode aufgerufen, nachdem Angular bereits Änderungen erkannt hat:

Einmal aufgerufen nach den ersten ngOnChanges ().

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:

constructor(private globalEventsService: GlobalEventsService) {

}

ngOnInit() {
    this.globalEventsService.showCheckoutHeader = true;
}

Dazu:

constructor(private globalEventsService: GlobalEventsService) {
    this.globalEventsService.showCheckoutHeader = true;
}

ngOnInit() {

}

und es hat das Problem behoben :)

Kevin LeStarge
quelle
3
Mein Problem war ähnlich. Dass ich nach langen Stunden einen Fehler gemacht und eine Variable außerhalb der ngOnInit-Funktion und des Konstruktors definiert habe. Dieser empfängt Datenänderungen von einem Observable, das in die Initalisierungsfunktion gestellt wird. Haben Sie dasselbe getan, um den Fehler zu beheben.
Ravo10
1
Rundum sehr ähnlich, aber ich habe versucht, router.navigatebeim Laden zu einem Fragment zu scrollen, wenn es in der URL vorhanden ist. Dieser Code wurde ursprünglich an der Stelle platziert, AfterViewInitan der ich den Fehler erhalten habe. Dann bin ich umgezogen, wie Sie dem Konstruktor gesagt haben, aber das Fragment wurde nicht berücksichtigt. Umzug in ngOnInitgelöst :) danke!
Joel Balmer
Was passiert, wenn mein HTML-Code über get ClockValue () {return DateTime.TimeAMPM (new Date ())} an eine Getter-Rückgabezeit als "HH: MM" gebunden ist? repariere das?
Meryan
Hier gilt das gleiche. Es wurde auch festgestellt, dass das Umschließen in setInterval()funktioniert auch, wenn es nach einem anderen lebenslangen Ereigniscode ausgelöst werden muss.
Rick Strahl
39

Aktualisieren

Ich empfehle dringend, zuerst mit der Selbstantwort des OP zu beginnen : Überlegen Sie genau, was im constructorVergleich zu dem, was im OP getan werden soll, getan werden kann ngOnChanges().

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:

<button *ngIf="form.pristine">Yo</button>

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: nonestattdessen Folgendes zu verwenden :

<button [style.display]="form.pristine ? 'inline' : 'none'">Yo</button>
Arnaud P.
quelle
6
Mein Verständnis des Unterschieds zwischen dem ngIf und dem Stil ist, dass der ngIf den HTML-Code erst dann in die Seite einbezieht, wenn die Bedingung erfüllt ist, wodurch das "Seitengewicht" ein kleines bisschen reduziert wird, während die Stiltechnik bewirkt, dass der HTML-Code immer so ist auf der Seite und wird einfach ausgeblendet oder basierend auf dem Wert von form.pristine angezeigt.
user3785010
4
Sie können auch [hidden]anstelle des sehr ausführlichen [style.display]Teils verwenden. :)
Philipp Meissner
2
Warum nicht. Obwohl, wie von @Simon_Weaver in einem anderen Kommentar auf dieser Seite erwähnt, [hidden] nicht immer das gleiche Verhalten wiedisplay: none
Arnaud P
1
Ich habe zwei verschiedene Schaltflächen (Abmelden / Anmelden) mit * ngIf in jeder Schaltfläche angezeigt, und dies hat das Problem verursacht.
GoTo
Konstrukteur war der richtige Ort für mich und startete eine materielle Snack-Bar
Austin
31

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, *ngIfnicht 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ügen constructor(private changeDetector : ChangeDetectorRef ) {}und dann

ngAfterViewChecked(){
  this.changeDetector.detectChanges();
}

Ich hoffe, das hilft anderen, da mir viele andere geholfen haben.

eper
quelle
3
Löst dies nicht eine Endlosänderungserkennungsschleife aus? Ich meine, Sie erkennen nach Änderungen, nachdem Sie überprüft haben.
Manuel Azar
@ ManuelAzar Anscheinend nicht. Dies ist die EINZIGE Lösung, die für mich funktioniert hat. Endlich ein bisschen Stille in meiner Konsole. Ich war so müde von all diesen irrelevanten "Fehlern" bei der Erkennung von Änderungen.
Jeremy Thille
31

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 mehr

Um dies zu korrigieren, können wir den AfterContentCheckedLife Cycle Hook und verwenden

import { ChangeDetectorRef, AfterContentChecked} from '@angular/core';

  constructor(
  private cdref: ChangeDetectorRef) { }

  ngAfterContentChecked() {

    this.cdref.detectChanges();

  }
Lijo
quelle
Obwohl dies das Problem beheben könnte, ist dies nicht sehr expansiv und übertrieben die CD?
Nicky
Ich denke, dies ist die einzige Antwort, die diesen Fehler behebt, der durch die Übergabe von Werten an ein Kind verursacht wird. Vielen Dank!
Java-Addict301
@ Nicky Ja. Jedes Mal, wenn Sie irgendwo auf dem Bildschirm berühren, wird ngAfterContentChecked () aufgerufen
Mert Mertce
25

In meinem Fall hatte ich dieses Problem in meiner Spezifikationsdatei, während ich meine Tests ausführte.

Ich musste Änderung ngIf zu [hidden]

<app-loading *ngIf="isLoading"></app-loading>

zu

<app-loading [hidden]="!isLoading"></app-loading>
Andre Evangelista
quelle
2
Der Unterschied besteht darin, dass *ngIfdas 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.
Grungondola
5
Aber das hat das eigentliche Problem nicht wirklich behoben ...?
Ravo10
23

Befolgen Sie die folgenden Schritte:

1. Verwenden Sie 'ChangeDetectorRef', indem Sie es wie folgt aus @ angle / core importieren:

import{ ChangeDetectorRef } from '@angular/core';

2. Implementieren Sie es wie folgt in constructor ():

constructor(   private cdRef : ChangeDetectorRef  ) {}

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:

functionName() {   
    yourCode;  
    //add this line to get rid of the error  
    this.cdRef.detectChanges();     
}
Chittrang Mishra
quelle
23

Ich habe ng2-Karussellamos (Angular 8 & Bootstrap 4) verwendet.

Unten wurde mein Problem behoben:

Was ich getan habe:

1. implement AfterViewChecked,  
2. add constructor(private changeDetector : ChangeDetectorRef ) {} and then 
3. ngAfterViewChecked(){ this.changeDetector.detectChanges(); }
Shahid Hussain Abbasi
quelle
Es half. Tolle!!
Pathik Vejani
Du hast meinen Tag gerettet ... Danke!
Omostan
19

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).

import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';

@Component({
    changeDetection: ChangeDetectionStrategy.OnPush
    selector: -
    ......
})
Dheeraj
quelle
Dies schien zu funktionieren, um Steuerelemente dynamisch hinzuzufügen / zu entfernen. Gibt es irgendwelche Nachteile?
Ricardo Saracino
Funktioniert wie ein Zauber in der Situation, die ich zur Hand habe, danke! Eine Komponente wurde an ein "globales" Objekt gebunden, das an anderer Stelle geändert wurde und den Fehler verursachte. Diese Komponente hatte bereits einen Update-Handler für die Aktualisierung des gebundenen Objekts. Dieser Event-Handler ruft jetzt changeDetectorRef.detectChanges () in Kombination mit ChangeDetectionStrategy.OnPush auf. Dies funktioniert wie gewünscht ohne Fehler.
Bernoulli IT
@ RicardoSaracino hast du irgendwelche Nachteile gefunden? Ich habe mich das Gleiche gefragt. Ich weiß, wie die Änderungserkennung von OnPush funktioniert, frage mich aber, ob es einen Fall gibt, den ich möglicherweise vermisse. Ich möchte nicht zurückkreisen müssen.
Mtpultz
@ RicardoSaracino, Ja, es hat einige Nachteile. Sie können auf diesen detaillierten Link verweisen. Blog.angular-university.io/onpush-change-detection-how-it-works
Dheeraj
@BernoulliIT Danke, ich bin froh, dass es bei dir funktioniert hat.
Dheeraj
17

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.

 setTimeout(() => {
        this.isLoading = true;
    });

3) Wenn Sie Ihren Code wirklich auf derselben VM ausführen möchten, verwenden Sie wie

Promise.resolve(null).then(() => this.isLoading = true);

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.

BEI IHRER
quelle
Können Sie Option 3 mit einem Stilausdruck verwenden? Ich habe einen Stilausdruck für die Höhe, der zuletzt ausgewertet werden sollte, da er auf injiziertem Inhalt basiert.
N-aß
1
Entschuldigung, habe gerade Ihren Kommentar gesehen, ja, ich sehe keinen Grund, warum nicht. Das sollte also auch bei Stiländerungen funktionieren.
ATHER
4

@HostBinding kann eine verwirrende Quelle für diesen Fehler sein.

Angenommen, Sie haben die folgende Hostbindung in einer Komponente

// image-carousel.component.ts
@HostBinding('style.background') 
style_groupBG: string;

Nehmen wir zur Vereinfachung an, diese Eigenschaft wird über die folgende Eingabeeigenschaft aktualisiert:

@Input('carouselConfig')
public set carouselConfig(carouselConfig: string) 
{
    this.style_groupBG = carouselConfig.bgColor;   
}

In der übergeordneten Komponente setzen Sie sie programmgesteuert ein ngAfterViewInit

@ViewChild(ImageCarousel) carousel: ImageCarousel;

ngAfterViewInit()
{
    this.carousel.carouselConfig = { bgColor: 'red' };
}

Folgendes passiert:

  • Ihre übergeordnete Komponente wird erstellt
  • Die ImageCarousel-Komponente wird erstellt und zugewiesen carousel(über ViewChild).
  • Wir können nicht zugreifen, carouselbis ngAfterViewInit()(es wird null sein)
  • Wir weisen die Konfiguration zu, die setzt style_groupBG = 'red'
  • Dies setzt wiederum background: reddie Host-ImageCarousel-Komponente ein
  • Diese Komponente gehört Ihrer übergeordneten Komponente. Wenn sie also nach Änderungen sucht, findet sie eine Änderung carousel.style.backgroundund 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.

ngAfterViewInit()
{
    this.carousel.carouselConfig = { ... };
    this.cdr.detectChanges();
}

Dies mag so offensichtlich aussehen und anderen Antworten sehr ähnlich sein, aber es gibt einen subtilen Unterschied.

Betrachten Sie den Fall, in dem Sie @HostBindingerst später während der Entwicklung hinzufügen . Plötzlich erhalten Sie diesen Fehler und es scheint keinen Sinn zu ergeben.

Simon_Weaver
quelle
2

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.

*ngIf="isProcessing()" 

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.

[hidden]="isProcessing()"

Bei der Verwendung [hidden]ändert sich das nicht physisch, DOMsondern es wird lediglich elementvor der Ansicht verborgen , höchstwahrscheinlich CSSim 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].

Kobus
quelle
Wenn Sie isProcessing()das tun, müssen Sie !isProcessing()für die[hidden]
Matthieu Charbonnier
hidden"verwendet kein CSS im Hintergrund", sondern eine reguläre HTML-Eigenschaft. developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/…
Lazar Ljubenović
1

Für mein Problem las ich github - "ExpressionChangedAfterItHasBeenCheckedError beim Ändern eines Komponenten-Nicht-Modell-Werts in afterViewInit" und beschloss, das ngModel hinzuzufügen

<input type="hidden" ngModel #clientName />

Es hat mein Problem behoben, ich hoffe es hilft jemandem.

Demodave
quelle
1
Wo auf dieser Seite heißt es hinzuzufügen ngModel. Und könnten Sie bitte erläutern, warum dies hilfreich sein sollte?
Peter Wippermann
Als ich dieses Problem aufspürte, untersuchte ich den Link. Nachdem ich den Artikel gelesen hatte, fügte ich das Attribut hinzu und mein Problem wurde behoben. Es ist hilfreich, wenn jemand auf dasselbe Problem stößt.
Demodave
1

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:

    console.log('EXPRESSIONCHANGED - HomePageComponent: constructor');
    console.log('EXPRESSIONCHANGED - HomePageComponent: setting config', newConfig);
    console.log('EXPRESSIONCHANGED - HomePageComponent: setting config ok');
    console.log('EXPRESSIONCHANGED - HomePageComponent: running detectchanges');

In den Rückrufen für Kinder / Dienste / Timer:

    console.log('EXPRESSIONCHANGED - ChildComponent: setting config');
    console.log('EXPRESSIONCHANGED - ChildComponent: setting config ok');

Wenn Sie detectChangesmanuell ausführen, fügen Sie auch dafür die Protokollierung hinzu:

    console.log('EXPRESSIONCHANGED - ChildComponent: running detectchanges');
    this.cdr.detectChanges();

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.

Geben Sie hier die Bildbeschreibung ein

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.

Simon_Weaver
quelle
1

In meinem Fall hatte ich eine asynchrone Eigenschaft LoadingServicemit einem BehavioralSubjectisLoading

Die Verwendung des [versteckten] Modells funktioniert, aber * ngIf schlägt fehl

    <h1 [hidden]="!(loaderService.isLoading | async)">
        THIS WORKS FINE
        (Loading Data)
    </h1>

    <h1 *ngIf="!(loaderService.isLoading | async)">
        THIS THROWS ERROR
        (Loading Data)
    </h1>
Mahesh
quelle
1

Eine Lösung, die bei mir mit rxjs funktioniert hat

import { startWith, tap, delay } from 'rxjs/operators';

// Data field used to populate on the html
dataSource: any;

....

ngAfterViewInit() {
  this.yourAsyncData.
      .pipe(
          startWith(null),
          delay(0),
          tap((res) => this.dataSource = res)
      ).subscribe();
}
Sandeep K Nair
quelle
Was war der problematische Code? Was ist hier die Lösung?
mkb
Hallo @mkb, das Problem trat auf, ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.wenn Wertänderungen ausgelöst werden, wenn sich das DOM ändert
Sandeep K Nair
Hallo, ich meine, was haben Sie hier getan, um das Problem zu lösen? Sie haben vorher überhaupt keine rxjs verwendet oder delay () oder startWith () hinzugefügt? Ich benutze rxjs bereits mit verschiedenen rxjs-Methoden, erhalte aber immer noch den Fehler. Ich hoffe, das Geheimnis zu lösen :(
mkb
Die hinzugefügten delaymachen den Fehler verschwinden. Es funktioniert ähnlich wie setTimeout.
Lazar Ljubenović
1

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 pinauf a zu ändern remove-circle, je nachdem, in welchem ​​Modus ein Bildschirm arbeitet.

Ich schätze, ich muss *ngIfstattdessen eine hinzufügen .

JGFMK
quelle
1

Mein Problem war offensichtlich, als ich hinzufügte, *ngIfaber das war nicht die Ursache. Der Fehler wurde verursacht, indem das Modell in {{}}Tags geändert wurde und *ngIfspäter versucht wurde, das geänderte Modell in der Anweisung anzuzeigen . Hier ist ein Beispiel:

<div>{{changeMyModelValue()}}</div> <!--don't do this!  or you could get error: ExpressionChangedAfterItHasBeenCheckedError-->
....
<div *ngIf="true">{{myModel.value}}</div>

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 Aufrufen changeMyModelValue(). Siehe https://angular.io/guide/component-interaction#parent-listens-for-child-event

goku_da_master
quelle
0

Ich hoffe, dies hilft jemandem, der hierher kommt: Wir führen Serviceabrufe auf ngOnInitfolgende Weise durch und verwenden eine Variable displayMain, um die Montage der Elemente im DOM zu steuern.

component.ts

  displayMain: boolean;
  ngOnInit() {
    this.displayMain = false;
    // Service Calls go here
    // Service Call 1
    // Service Call 2
    // ...
    this.displayMain = true;
  }

und component.html

<div *ngIf="displayMain"> <!-- This is the Root Element -->
 <!-- All the HTML Goes here -->
</div>
retr0
quelle
0

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.

Shreekar Hegde
quelle
0

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.

Muneem Habib
quelle
0

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.

Deunz
quelle
Verwenden hiddenstatt ngIfist kein Hack, und es geht auch nicht auf den Kern des Problems ein. Sie maskieren nur das Problem.
Lazar Ljubenović
-2

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

user998548
quelle
1
Dies beantwortet nicht nur die Frage nicht, es ist auch ein schrecklicher Rat. Leute, bitte implementiert rxjs nicht selbst neu, nur weil ihr Angulars CD-Fehler bekommt. :)
Lazar Ljubenović