Vă rog să-mi explici de ce tot primesc aceasta eroare: ExpressionChangedAfterItHasBeenCheckederror: Expresie a schimbat dupa ce a fost verificat.
Evident, nu doar în dev mode, nu't să se întâmple și de producție a construi, dar's foarte enervant și eu pur și simplu nu't înțeleagă beneficiile de a avea o eroare în dev mediu care a câștigat't apar pe prod ... probabil din cauza de lipsa mea de înțelegere.
De obicei, fix este destul de usor, am doar folie de eroare cauzează cod într-un setTimeout astfel:
setTimeout(()=> {
this.isLoading = true;
}, 0);
Sau de forță detecta schimbări cu un constructor astfel de prognoze: constructor(privat cd: ChangeDetectorRef) {}
:
this.isLoading = true;
this.cd.detectChanges();
Dar de ce am rula în mod constant în această eroare? Vreau să înțeleg, așa că am putea evita aceste hacky fixat în viitor.
Această eroare indică o problemă reală în cererea dumneavoastră, prin urmare, este logic pentru a arunca o excepție.
În devMode
schimbare de detectare adaugă o suplimentare rândul său, după fiecare schimbare periodică de detectare a alerga pentru a verifica dacă modelul s-a schimbat.
În cazul în care modelul s-a schimbat între periodice și suplimentare de detectare a schimbării rândul său, acest lucru indică faptul că fie
care sunt atât de rău, pentru că nu este clar cum să procedeze pentru că modelul s-ar putea stabiliza niciodată.
Dacă Unghiulare se execută de detectare schimbare până când modelul se stabilizează, s-ar putea rula pentru totdeauna. Dacă Unghiulare nu't run detectare schimbare, atunci ecranul ar putea să nu reflecte starea curentă a modelului.
A se vedea, de asemenea, https://stackoverflow.com/questions/34868810/what-is-difference-between-production-and-development-mode-in-angular2/34868896#34868896
O mulțime de înțelegere a venit după ce am înțeles Unghiulară a Ciclului de viață Cârlige și relația lor cu schimbare de detectare.
Am fost încercarea de a obține Unghiulare pentru a actualiza un steag globale legat de *ngIf
de un element, și am fost încercarea de a schimba steagul interiorul ngOnInit()
ciclul de viață cârlig de o altă componentă.
Potrivit documentației, această metodă este numit după Unghiulară a detectat deja modificări:
a Sunat o dată, după prima ngOnChanges().
Astfel actualizarea pavilion în interiorul ngOnChanges()` a câștigat't iniția schimbare de detectare. Apoi, odată ce schimbare de detectare a declanșat în mod natural, din nou, steagul's valoare s-a schimbat și eroare este aruncat.
În cazul meu, am schimbat asa:
constructor(private globalEventsService: GlobalEventsService) {
}
ngOnInit() {
this.globalEventsService.showCheckoutHeader = true;
}
La acest lucru:
constructor(private globalEventsService: GlobalEventsService) {
this.globalEventsService.showCheckoutHeader = true;
}
ngOnInit() {
}
și a rezolvat problema :)
Update
Am foarte recomandăm să începeți cu OP's auto răspuns primul: corect cred că despre ceea ce poate fi făcut în constructorul
vs ce ar trebui făcut în ngOnChanges()
.
*Original ***
Acest lucru este mai mult o notă mult de un răspuns, dar l-ar putea ajuta cineva. Am dat peste această problemă atunci când încearcă să facă prezența unui buton depinde de stare de forma:
<button *ngIf="form.pristine">Yo</button>
Din câte știu, această sintaxă duce la butonul adăugate și eliminate din DOM bazate pe starea. Care, la rândul său, duce la ExpressionChangedAfterItHasBeenCheckederror
.
Fix în cazul meu (desi eu nu't pretinde pentru a înțelege pe deplin implicațiile diferența), a fost de a folosi display: none` în loc:
<button [style.display]="form.pristine ? 'inline' : 'none'">Yo</button>
Au fost răspunsuri interesante dar n-am't par pentru a găsi unul pentru a se potrivi nevoilor mele, cea mai apropiată fiind la @chittrang-mishra, care se referă numai la o anumită funcție și nu mai multe comută la fel ca în aplicația mea.
Nu am vrut să folosesc[ascuns]pentru a profita de
*ngIf` nu chiar de a fi o parte din DOM așa că am găsit următoarea soluție care nu poate fi mai bun pentru toți, deoarece suprimă de eroare în loc să o corecteze, dar în cazul meu, în cazul în care știu că rezultatul final este corect, se pare ok pentru aplicația mea.
Ceea ce am făcut a fost să pună în aplicare AfterViewChecked
, adaugă constructor(privat changeDetector : ChangeDetectorRef ) {}` și apoi
ngAfterViewChecked(){
this.changeDetector.detectChanges();
}
Sper că acest lucru ajută alte ca multe altele, m-au ajutat.
În cazul meu, am avut această problemă în fișier spec, în timp ce rulează testele mele.
Am avut de a schimba ngIf
a [ascuns]
<app-loading *ngIf="isLoading"></app-loading>
pentru
<app-loading [hidden]="!isLoading"></app-loading>
Urmați pașii de mai jos:
import{ ChangeDetectorRef } from '@angular/core';
constructor( private cdRef : ChangeDetectorRef ) {}
functionName() {
yourCode;
//add this line to get rid of the error
this.cdRef.detectChanges();
}
Am fost confruntă cu aceeași problemă ca valoarea se schimbă într-una de matrice în componentă. Dar în loc de a detecta schimbările pe valoarea schimba, am schimbat componenta de detectare a schimbării de strategie a onPush
(care va detecta schimbări în schimbare obiect și nu pe valoarea schimba).
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush
selector: -
......
})
Unghiulare se execută de detectare a schimbării și atunci când își găsi că unele valori care a fost trecut la copil componentă a fost schimbat Unghiulare aruncă eroare:
ExpressionChangedAfterItHasBeenCheckederror
click pentru mai multe
În scopul de a corecta acest lucru, putem folosi `AfterContentChecked ciclului de viață al cârlig și
import { ChangeDetectorRef, AfterContentChecked} from '@angular/core';
constructor(
private cdref: ChangeDetectorRef) { }
ngAfterContentChecked() {
this.cdref.detectChanges();
}
Referindu-se la art. https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
Deci mecanica de detectare a schimbării de fapt, funcționează într-un mod atât de detectare a schimbării și de verificare a digera sunt efectuate sincron. Asta înseamnă că, dacă vom actualiza proprietățile asincron valorile nu vor fi actualizate atunci când verificarea bucla se execută și nu vom primi ExpressionChanged...
eroare. Motivul pentru care avem această eroare este, în timpul procesului de verificare, Unghiulare vede valori diferite atunci ceea ce este înregistrat timpul de detectare a schimbării de fază. Ca să evite asta....
Utilizarea changeDetectorRef
utilizarea setTimeOut. Acest lucru va executa codul într-un alt NEG ca un macro-sarcina. Unghiulare nu vor vedea aceste schimbări în procesul de verificare și nu vei da eroare.
setTimeout(() => {
this.isLoading = true;
});
Promise.resolve(null).then(() => this.isLoading = true);
Acest lucru va crea un micro-sarcina. Micro-coada sarcina este prelucrat după curent sincron cod a terminat de executare, prin urmare, actualizarea la proprietate se va întâmpla după verificarea pas.
@HostBinding
poate fi confuz sursă de această eroare.De exemplu, să spunem că aveți următoarele gazdă obligatoriu într-o componentă
// image-carousel.component.ts
@HostBinding('style.background')
style_groupBG: string;
Pentru simplitate, să spunem că această proprietate este actualizat prin următoarele proprietate de intrare:
@Input('carouselConfig')
public set carouselConfig(carouselConfig: string)
{
this.style_groupBG = carouselConfig.bgColor;
}
În cadrul componentei părinte ești programatic stabilind-o în ngAfterViewInit
@ViewChild(ImageCarousel) carousel: ImageCarousel;
ngAfterViewInit()
{
this.carousel.carouselConfig = { bgColor: 'red' };
}
Aici's ce se întâmplă :
ngAfterViewInit()
(va fi null)style_groupBG = 'roșu'
O soluție este de a introduce un alt înveliș div insider ImageCarousel și setați culoarea de fundal pe asta, dar atunci nu't obține unele dintre beneficiile de a utiliza HostBinding
(cum ar fi permițându-mamă pentru a controla pe deplin limitele obiectului).
Cea mai bună soluție, în cadrul componentei părinte este de a adăuga detectChanges() după setarea de configurare.
ngAfterViewInit()
{
this.carousel.carouselConfig = { ... };
this.cdr.detectChanges();
}
Acest lucru poate arata destul de evident ca asta, și foarte asemănătoare cu alte răspunsuri, dar nu's o diferență subtilă.
Luați în considerare cazul în care nu't add `@HostBinding până mai târziu în dezvoltare. Dintr-o dată tu a lua această eroare și nu't părea să aibă vreun sens.
O soluție care a lucrat pentru mine, folosind rxjs ``typescript import { începem, atingeți ușor, întârziere } din 'rxjs/operatorii';
// Câmp de date folosit pentru a popula html sursa de date: orice;
....
ngAfterViewInit() { acest lucru.yourAsyncData. .țeavă( începem(null), întârziere(0), atingeți((res) => acest lucru.dataSource = res) ).aboneaza-te(); } ``
Această eroare poate fi destul de confuz, și-l's ușor de a face o presupunere greșită despre exact când l's apar. Mi se pare util pentru a adăuga o mulțime de depanare afirmații de genul asta de-a lungul afectate componente în locurile corespunzătoare. Acest lucru ajută înțelege fluxul.
În părinte pus declaratii de genul asta (exact string 'EXPRESSIONCHANGED' este important), dar altele decât că acestea sunt doar exemple:
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');
În copil / servicii / timer callback:
console.log('EXPRESSIONCHANGED - ChildComponent: setting config');
console.log('EXPRESSIONCHANGED - ChildComponent: setting config ok');
Dacă tu a alerga detectChanges
adăugați manual de logare pentru asta:
console.log('EXPRESSIONCHANGED - ChildComponent: running detectchanges');
this.cdr.detectChanges();
Apoi, în Chrome debugger doar filtru de 'EXPRESSIONCHANGES'. Acest lucru vă va arăta exact fluxul și scopul a tot ceea ce devine, set, și, de asemenea, exact la ce punct Unghiular aruncă eroare.
De asemenea, puteți să faceți clic pe link-uri gri pentru a pune puncte de întrerupere în.
Un alt lucru să urmăriți dacă aveți în mod similar numit proprietăți de-a lungul aplicația dumneavoastră (cum ar fi stilul lui.fond`) asigurați-vă că're depanare cea crezi că - prin setarea-l la un obscur valoare de culoare.
Problema mea a fost vădită atunci când am adăugat *ngIf
dar asta era't cauza. Eroarea a fost cauzată de schimbarea modelului în {{}}
tag-uri apoi încearcă pentru a afișa schimbat modelul din *ngIf
declarație mai târziu. Aici's un exemplu:
<div>{{changeMyModelValue()}}</div> <!--don't do this! or you could get error: ExpressionChangedAfterItHasBeenCheckedError-->
....
<div *ngIf="true">{{myModel.value}}</div>
Pentru a remedia problema, am schimbat unde am sunat pentru changeMyModelValue()` a un loc care a făcut mai mult sens.
În situația mea, am vrut changeMyModelValue()numit ori de câte ori un copil componentă a schimbat datele. Acest lucru a necesitat am crea și de a emite un eveniment în care copilul componentă atât de mamă, ar descurca (de asteptare
changeMyModelValue()`. vezi https://angular.io/guide/component-interaction#parent-listens-for-child-event
Pentru problema mea, am citit github - "ExpressionChangedAfterItHasBeenCheckederror când se schimbă o component 'non modelul' valoarea în afterViewInit" și a decis pentru a adăuga ngModel
<input type="hidden" ngModel #clientName />
E fix problema mea, sper sa ajute pe cineva.
Am avut acest tip de eroare în Ionic3 (care foloseste Unghiulare 4 ca parte a l's tehnologie de stivă).
Pentru mine a fost să fac acest lucru:
<ion-pictograma [name]="getFavIconName()"></ion-icon>
Așa că am încercat să condiționat schimba tipul unei ion-icon de la un " pin " pentru a o elimina-cerc`, pe un modul de un ecran a fost de operare pe.
Am'm a că am'll trebuie să adăugați o *ngIf
în loc.
În cazul meu, am avut o asincron de proprietate în LoadingService cu un BehavioralSubject
isLoading`
Folosind [ascuns] modelul funcționează, dar *ngIf nu
<h1 [hidden]="!(loaderService.isLoading | async)">
THIS WORKS FINE
(Loading Data)
</h1>
<h1 *ngIf="!(loaderService.isLoading | async)">
THIS THROWS ERROR
(Loading Data)
</h1>
Aici's gândurile mele cu privire la ceea ce se întâmplă. Nu am citit documentatia, dar sunt sigur că asta este o parte din motivul de eroare este afișat.
*ngIf="isProcessing()"
Atunci când se utilizează *ngIf, se schimbă fizic DOM prin adăugarea sau eliminarea elementului de fiecare dată când se schimbă starea. Deci, dacă starea se schimbă înainte de a se prestate la punctul de vedere (ceea ce este foarte posibil, în Unghiular's world), eroarea este aruncat. A se vedea explicația de aici între dezvoltare și producție moduri.
[hidden]="isProcessing()"
Atunci când se utilizează [ascuns]
, nu schimba fizic DOM
ci doar ascunde element
de vedere, cel mai probabil folosind CSS
în spate. Elementul este încă acolo, în DOM, dar nu sunt vizibile în funcție de starea's valoare. De aceea, eroarea nu va apărea atunci când se utilizează [ascuns]
.
Sper că acest lucru ajută cineva vine aici:
Vom face apeluri în ngOnInit în felul următor și de a folosi o variabila
displayMain` pentru a controla Montarea de elemente de la DOM.
componenta.ts
displayMain: boolean;
ngOnInit() {
this.displayMain = false;
// Service Calls go here
// Service Call 1
// Service Call 2
// ...
this.displayMain = true;
}
și component.html
<div *ngIf="displayMain"> <!-- This is the Root Element -->
<!-- All the HTML Goes here -->
</div>