Warum ist die Komponente in diesem einfachen Plunk
@Component({
selector: 'my-app',
template: `<div>I'm {{message}} </div>`,
})
export class App {
message:string = 'loading :(';
ngAfterViewInit() {
this.updateMessage();
}
updateMessage(){
this.message = 'all done loading :)'
}
}
Werfen:
AUSNAHME: Der Ausdruck 'Ich bin {{message}} in App @ 0: 5' hat sich geändert, nachdem er überprüft wurde. Vorheriger Wert: 'Ich lade :('. Aktueller Wert: 'Ich bin fertig mit Laden :)' in [Ich bin {{message}} in App @ 0: 5]
Wenn ich nur eine einfache Bindung aktualisiere, wenn meine Ansicht initiiert wird?
typescript
angular
zog mehr
quelle
quelle
ExpressionChangedAfterItHasBeenCheckedError
Fehler wissen müssen, erklärt das Verhalten ausführlich.ChangeDetectionStrategy
wenn SiedetectChanges()
stackoverflow.com/questions/39787038/…Antworten:
Wie von drawmoore angegeben, besteht die richtige Lösung in diesem Fall darin, die Änderungserkennung für die aktuelle Komponente manuell auszulösen. Dies erfolgt mithilfe der
detectChanges()
Methode desChangeDetectorRef
Objekts (importiert vonangular2/core
) oder seinermarkForCheck()
Methode, mit der auch alle übergeordneten Komponenten aktualisiert werden. Relevantes Beispiel :Hier finden Sie auch Plunker, die die Ansätze ngOnInit , setTimeout und enableProdMode für alle Fälle demonstrieren .
quelle
cdr : any
oder so etwas unter dermessage
Erklärung zu sehen. Nur besorgt, dass ich etwas verpasst habe?Beachten Sie zunächst, dass diese Ausnahme nur ausgelöst wird, wenn Sie Ihre App im Entwicklungsmodus ausführen (was ab Beta-0 standardmäßig der Fall ist): Wenn Sie
enableProdMode()
beim Booten der App aufrufen , wird sie nicht ausgelöst ( siehe) aktualisiert plunk ).Zweitens, tu das nicht da diese Ausnahme aus gutem Grund ausgelöst wird: Kurz gesagt, im Dev-Modus folgt auf jede Änderungserkennungsrunde sofort eine zweite Runde, in der überprüft wird, ob sich seit dem Ende der ersten keine Bindungen geändert haben. Dies würde darauf hinweisen, dass Änderungen durch die Änderungserkennung selbst verursacht werden.
In Ihrem Plunk wird die Bindung
{{message}}
durch Ihren Aufruf an geändertsetMessage()
, was imngAfterViewInit
Hook geschieht , der als Teil der anfänglichen Änderungserkennungsrunde auftritt. Das an sich ist jedoch nicht problematisch - das Problem ist dassetMessage()
die Bindung geändert aber keine neue Runde der Änderungserkennung ausgelöst wird. Dies bedeutet, dass diese Änderung erst erkannt wird, wenn eine zukünftige Änderungsrunde an einer anderen Stelle ausgelöst wird.Das Mitnehmen: Alles, was eine Bindung ändert, muss eine Runde der Änderungserkennung auslösen, wenn dies der Fall ist.
Aktualisieren Sie als Antwort auf alle Anfragen nach einem Beispiel dafür : Die Lösung von @ Tycho funktioniert ebenso wie die drei Methoden in der Antwort, auf die @MarkRajcok hingewiesen hat. Aber ehrlich gesagt fühlen sich alle hässlich und falsch für mich an, wie die Art von Hacks, an die wir uns in ng1 gewöhnt haben.
Zwar gibt es gelegentliche Umstände, unter denen diese Hacks angemessen sind, aber wenn Sie sie nur gelegentlich verwenden, ist dies ein Zeichen dafür, dass Sie gegen das Framework kämpfen, anstatt dessen reaktive Natur voll zu nutzen.
IMHO, eine idiomatischere "Angular2-Methode", um dies zu erreichen, ist etwas in der Art von: ( plunk )
quelle
detectChanges()
.updateMessage
setMessage
ngAfterViewChecked()
arbeitete für mich:quelle
Ich habe dies behoben, indem ich ChangeDetectionStrategy aus dem Winkelkern hinzugefügt habe.
quelle
CheckOnce
( Dokumentation ) eingestelltthis.cdr.detectChanges();
nach dem, was Sie laden möchten . Weil es sein könnte, dass die Änderungserkennung nicht ausgelöst wirdKönnen Sie nicht verwenden,
ngOnInit
weil Sie nur die Mitgliedsvariable ändernmessage
?Wenn Sie auf einen Verweis auf eine untergeordnete Komponente zugreifen möchten
@ViewChild(ChildComponent)
, müssen Sie tatsächlich mit darauf wartenngAfterViewInit
.Eine schmutzige Lösung besteht darin, die
updateMessage()
in der nächsten Ereignisschleife mit z. B. setTimeout aufzurufen.quelle
Dafür habe ich die obigen Antworten ausprobiert. Viele funktionieren in der neuesten Version von Angular (6 oder höher) nicht.
Ich verwende die Materialkontrolle, für die nach der ersten Bindung Änderungen erforderlich waren.
Wenn ich meine Antwort hinzufüge, hilft dies einigen, bestimmte Probleme zu lösen.
quelle
Der Artikel Alles, was Sie über den
ExpressionChangedAfterItHasBeenCheckedError
Fehler wissen müssen, erklärt das Verhalten ausführlich.Das Problem bei Ihrer Einrichtung besteht darin, dass der
ngAfterViewInit
Lifecycle-Hook ausgeführt wird, nachdem die Änderungserkennung DOM-Aktualisierungen verarbeitet hat. Und Sie ändern effektiv die Eigenschaft, die in der Vorlage in diesem Hook verwendet wird, was bedeutet, dass DOM neu gerendert werden muss:und dies erfordert einen weiteren Änderungserkennungszyklus, und Angular führt konstruktionsbedingt nur einen Digest-Zyklus aus.
Grundsätzlich haben Sie zwei Alternativen, um das Problem zu beheben:
Aktualisieren Sie die Eigenschaft asynchron entweder mit
setTimeout
,Promise.then
oder asynchron beobachtbar in der Vorlage referenziertFühren Sie die Eigenschaftsaktualisierung in einem Hook vor der DOM-Aktualisierung durch - ngOnInit, ngDoCheck, ngAfterContentInit, ngAfterContentChecked.
quelle
Sie müssen nur Ihre Nachricht in den rechten Lifecycle Haken aktualisieren, ist in diesem Fall
ngAfterContentChecked
stattngAfterViewInit
, weil in ngAfterViewInit eine Prüfung für die variable Nachricht gestartet wurde , aber noch nicht beendet.Siehe: https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#afterview
Der Code lautet also nur:
Siehe die Arbeitsdemo zu Plunker.
quelle
@ViewChildren()
Zählers verwendet, der von einem Observable gefüllt wurde. Dies war die einzige Lösung, die für mich funktioniert hat!ngAfterContentChecked
undChangeDetectorRef
von oben hat bei mir funktioniert. AmngAfterContentChecked
angerufenen -this.cdr.detectChanges();
Dieser Fehler tritt auf, weil der vorhandene Wert unmittelbar nach der Initialisierung aktualisiert wird. Wenn Sie also einen neuen Wert aktualisieren, nachdem der vorhandene Wert in DOM gerendert wurde, funktioniert dies einwandfrei. Wie in diesem Artikel erwähnt. Angular Debugging "Ausdruck hat sich geändert, nachdem er überprüft wurde."
Zum Beispiel können Sie verwenden
}}
oder
Wie Sie sehen können, habe ich die Zeit in der setTimeout-Methode nicht erwähnt. Da es sich um eine vom Browser bereitgestellte API handelt, nicht um eine JavaScript-API, wird diese im Browser-Stack separat ausgeführt und wartet, bis die Elemente des Aufrufstapels abgeschlossen sind.
Wie die Browser-API das Konzept hervorruft, erklärt Philip Roberts in einem der Youtube-Videos (Was ist der Hack ist eine Ereignisschleife?).
quelle
Sie können updateMessage () auch in der ngOnInt () - Methode aufrufen, zumindest funktioniert es bei mir
In RC1 löst dies keine Ausnahme aus
quelle
Sie können auch einen Timer mit der
Observable.timer
Funktion rxjs erstellen und dann die Nachricht in Ihrem Abonnement aktualisieren:quelle
Es wird ein Fehler ausgegeben, da Ihr Code aktualisiert wird, wenn ngAfterViewInit () aufgerufen wird. Bedeutet, dass Ihr Anfangswert geändert wurde, als ngAfterViewInit stattfand. Wenn Sie dies in ngAfterContentInit () aufrufen, wird kein Fehler ausgegeben .
quelle
Ich konnte @Biranchis Beitrag nicht kommentieren, da ich nicht genug Ruf habe, aber es hat das Problem für mich behoben.
Eine Sache zu beachten! Wenn das Hinzufügen von changeDetection: ChangeDetectionStrategy.OnPush für die Komponente nicht funktioniert hat und es sich um eine untergeordnete Komponente (dumme Komponente) handelt, versuchen Sie, sie auch dem übergeordneten Element hinzuzufügen.
Dies hat den Fehler behoben, aber ich frage mich, was die Nebenwirkungen davon sind.
quelle
Ich habe einen ähnlichen Fehler beim Arbeiten mit datatable erhalten. Wenn Sie * ngFor in einer anderen * ngFor-Datentabelle verwenden, wird dieser Fehler ausgelöst, wenn der Winkeländerungszyklus unterbrochen wird. Verwenden Sie also anstelle von datatable in datatable eine reguläre Tabelle oder ersetzen Sie mf.data durch den Array-Namen. Das funktioniert gut.
quelle
Ich denke, eine einfachste Lösung wäre wie folgt:
(static working: boolean)
in der Klasse, in der diese Funktion vorhanden ist, und machen Sie sie bei jedem Aufruf der Funktion einfach wahr, je nachdem, was Sie möchten. Wenn der Wert der Arbeit innerhalb der Funktion wahr ist, kehren Sie einfach sofort zurück, ohne etwas zu tun. Andernfalls führen Sie die gewünschte Aufgabe aus. Stellen Sie sicher, dass Sie diese Variable nach Abschluss der Aufgabe in false ändern, dh am Ende der Codezeile oder innerhalb der Subscribe-Methode, wenn Sie mit dem Zuweisen von Werten fertig sind!quelle