ExpressionChangedAfterItHasBeenCheckederror Explained

Proszę wyjaśnij mi dlaczego ciągle dostaję ten błąd: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.

Oczywiście, dostaję go tylko w trybie dev, nie dzieje się to na mojej kompilacji produkcyjnej, ale jest to bardzo denerwujące i po prostu nie rozumiem korzyści płynących z błędu w moim środowisku dev, który nie pojawi się na prod-prawdopodobnie z powodu mojego braku zrozumienia.

Zazwyczaj poprawka jest dość łatwa, po prostu zawijam kod powodujący błąd w setTimeout, Tak:

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

Lub wymusić wykrycie zmian za pomocą konstruktor jak ten: constructor(private cd: ChangeDetectorRef) {}:

this.isLoading = true;
this.cd.detectChanges();

Ale dlaczego ciągle napotykam ten błąd? Chcę to zrozumieć, żeby uniknąć tych hakerskich poprawek w przyszłości.

Author: Rohit Sharma, 2017-04-12

14 answers

Miałem podobny problem. Przeglądając dokumentację lifecycle hooks , zmieniłem ngAfterViewInit na ngAfterContentInit i zadziałało.

 51
Author: onlyme,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-22 04:24:24

Ten błąd wskazuje na prawdziwy problem w Twojej aplikacji, dlatego warto wyrzucić wyjątek.

In devMode wykrywanie zmian dodaje dodatkowy obrót po każdym regularnym uruchomieniu wykrywania zmian, aby sprawdzić, czy Model się zmienił.

Jeśli model zmienił się między zwykłym i dodatkowym obrotem detekcji zmiany, oznacza to, że albo

  • samo wykrywanie zmian spowodowało zmianę
  • metoda lub getter zwraca inną wartość co czas nazywa się

Które są złe, ponieważ nie jest jasne, jak postępować, ponieważ model może nigdy się nie ustabilizować.

Jeśli kątowe biegi zmieniają detekcję, dopóki model się nie ustabilizuje, może działać w nieskończoność. Jeśli funkcja Angular nie uruchamia detekcji zmian, Widok może nie odzwierciedlać bieżącego stanu modelu.

Zobacz także Jaka jest różnica między trybem produkcji i rozwoju w Angular2?

 49
Author: Günter Zöchbauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:26

To bardziej na marginesie niż odpowiedź, ale może komuś pomóc. Natknąłem się na ten problem, próbując uzależnić obecność przycisku od stanu formularza:

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

Z tego co wiem, ta składnia prowadzi do dodania i usunięcia przycisku z DOM na podstawie warunku. Co z kolei prowadzi do ExpressionChangedAfterItHasBeenCheckedError.

Fix w moim przypadku (choć nie twierdzę, że rozumiem pełne konsekwencje różnicy), było użycie display: none zamiast:

<button [style.display]="form.pristine ? 'inline' : 'none'">Yo</button>
 20
Author: Arnaud P,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-01 07:06:46

Wiele zrozumienia przyszło, gdy zrozumiałemkątowe Haki cyklu życia i ich związek z wykrywaniem zmian.

Próbowałem uzyskać Angular, aby zaktualizować globalną flagę związaną z *ngIf elementu, i próbowałem zmienić tę flagę wewnątrz ngOnInit() Hooka cyklu życia innego komponentu.

Zgodnie z dokumentacją, metoda ta jest wywoływana po wykryciu zmian przez Angular:

Wywołany raz, po pierwszym ngOnChanges ().

Więc aktualizacja flagi wewnątrz ngOnChanges() nie spowoduje wykrycia zmian. Następnie, po ponownym uruchomieniu wykrywania zmian, wartość flagi uległa zmianie I błąd jest wyrzucany.

W moim przypadku zmieniłem to:

constructor(private globalEventsService: GlobalEventsService) {

}

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

Do tego:

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

ngOnInit() {

}

I naprawił problem:)

 20
Author: Kevin LeStarge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-11 21:52:18

W moim przypadku, miałem ten problem w moim pliku spec, podczas wykonywania moich testów.

I had to change ngIf na [hidden]

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

Do

<app-loading [hidden]="!isLoading"></app-loading>
 12
Author: Andre Evangelista,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-10 21:48:21

Miałem do czynienia z tym samym problemem, co zmiana wartości w jednej z tablic w moim komponencie. Ale zamiast wykrywania zmian przy zmianie wartości, zmieniłem strategię wykrywania zmian w komponencie na onPush (która wykryje zmiany przy zmianie obiektu, a nie przy zmianie wartości).

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

@Component({
    changeDetection: ChangeDetectionStrategy.OnPush
    selector: -
    ......
})
 8
Author: Dheeraj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-12 22:40:11

Wykonaj poniższe kroki:

1. Użyj 'ChangeDetectorRef' importując go z @angular/core w następujący sposób:

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

2. Zaimplementuj go w constructor () w następujący sposób:

constructor(   private cdRef : ChangeDetectorRef  ) {}

3. Dodaj następującą metodę do funkcji, którą wywołujesz podczas zdarzenia, takiego jak kliknięcie przycisku. Tak to wygląda:

functionName() {   
    yourCode;  
    //add this line to get rid of the error  
    this.cdRef.detectChanges();     
}
 7
Author: Chittrang Mishra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-01 12:08:45

[[3]] nawiązując do artykułu https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4

Tak więc mechanika wykrywania zmian faktycznie działa w taki sposób, że zarówno wykrywanie zmian, jak i weryfikacja są wykonywane synchronicznie. Oznacza to, że jeśli zaktualizujemy właściwości asynchronicznie, wartości nie będą aktualizowane, gdy uruchomiona zostanie pętla weryfikacji i nie otrzymamy błędu ExpressionChanged.... Powód, dla którego my błąd ten polega na tym, że podczas procesu weryfikacji kątowy widzi różne wartości, a następnie to, co zarejestrował podczas fazy wykrywania zmian. Żeby tego uniknąć....

1) Użyj changeDetectorRef

2) Użyj setTimeOut. Spowoduje to wykonanie kodu w innej maszynie wirtualnej jako makro-zadanie. Angular nie zobaczy tych zmian podczas procesu weryfikacji i nie otrzymasz tego błędu.

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

3) jeśli naprawdę chcesz wykonać swój kod na tej samej maszynie wirtualnej użyj np.

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

To stworzy mikro-zadanie. Kolejka mikro-zadań jest przetwarzana po zakończeniu wykonywania bieżącego kodu synchronicznego, dlatego aktualizacja do właściwości nastąpi po etapie weryfikacji.

 4
Author: ATHER,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-12 21:55:13

Miałem tego rodzaju błąd w Ionic3(który używa Angular 4 jako części stosu technologii).

Dla mnie to było to:

<ion-icon [name]="getFavIconName()"></ion-icon>

Więc próbowałem warunkowo zmienić typ ion-icon z pin na remove-circle, w zależności od trybu, na którym działał ekran.

Zgaduję, że będę musiał zamiast tego dodać *ngIF.

 1
Author: JGFMK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-18 17:58:08

Dla mojego problemu, czytałem github - "ExpressionChangedAfterItHasBeenCheckederror podczas zmiany wartości komponentu' non model 'w afterViewInit" i zdecydowałem się dodać ngModel

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

To naprawiło mój problem, mam nadzieję, że komuś pomoże.

 0
Author: Demodave,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-13 14:57:31

Oto moje przemyślenia na temat tego, co się dzieje. Nie przeczytałem dokumentacji, ale jestem pewien, że jest to część tego, dlaczego wyświetlany jest błąd.

*ngIf="isProcessing()" 

Podczas używania *ngIf, fizycznie zmienia DOM, dodając lub usuwając element za każdym razem, gdy zmienia się warunek. Jeśli więc warunek zmieni się zanim zostanie renderowany do widoku (co jest wysoce możliwe w świecie Angular), błąd zostanie wyrzucony. Zobacz Wyjaśnienie tutaj pomiędzy trybami rozwoju i produkcji.

[hidden]="isProcessing()"

Kiedy użycie [hidden] nie zmienia fizycznie DOM, a jedynie ukrywa element przed widokiem, najprawdopodobniej używając CSS z tyłu. Element nadal znajduje się w DOM, ale nie jest widoczny w zależności od wartości warunku. Dlatego błąd nie wystąpi podczas używania [hidden].

 0
Author: Kobus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-07 03:55:25

@HostBinding może być mylące źródło tego błędu.

Na przykład, powiedzmy, że masz następujące powiązanie hosta w komponencie

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

Dla uproszczenia, powiedzmy, że ta właściwość jest aktualizowana za pomocą następującej właściwości wejściowej:

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

W komponencie nadrzędnym ustawiasz go programowo w ngAfterViewInit

@ViewChild(ImageCarousel) carousel: ImageCarousel;

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

Oto co się dzieje:

  • tworzony jest komponent nadrzędny
  • komponent ImageCarousel jest tworzony i przypisywany do carousel (via ViewChild)
  • nie możemy uzyskać dostępu carousel dopóki ngAfterViewInit() (będzie null)
  • przypisujemy konfigurację, która ustawia style_groupBG = 'red'
  • To z kolei ustawia background: red na komponencie hosta ImageCarousel
  • ten komponent jest 'własnością' twojego komponentu nadrzędnego, więc kiedy sprawdza zmiany, znajduje zmianę na carousel.style.background i nie jest wystarczająco sprytny, aby wiedzieć, że nie jest to problem, więc rzuca wyjątek.

Jednym z rozwiązań jest wprowadzenie innego wrappera div insider ImageCarousel i ustaw na nim kolor tła, ale wtedy nie uzyskasz niektórych korzyści z używania HostBinding (takich jak umożliwienie rodzicowi kontrolowania pełnych granic obiektu).

Lepszym rozwiązaniem, w komponencie nadrzędnym jest dodanie detectChanges () po ustawieniu konfiguracji.

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

To może wyglądać dość oczywiste i bardzo podobne do innych odpowiedzi, ale jest subtelna różnica.

Rozważ przypadek, w którym nie dodasz @HostBinding dopóki później podczas rozwoju. Nagle pojawia się ten błąd i wydaje się, że nie ma to żadnego sensu.

 0
Author: Simon_Weaver,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-02 21:01:21

Wskazówki debugowania

Ten błąd może być dość mylący i łatwo jest przyjąć błędne założenie o tym, kiedy dokładnie się pojawia. Uważam, że pomocne jest dodanie wielu instrukcji debugowania, takich jak to, w całym dotkniętych komponentach w odpowiednich miejscach. Pomaga to zrozumieć przepływ.

W rodzicu umieść instrukcje takie jak to (dokładny łańcuch 'EXPRESSIONCHANGED' jest ważny), ale poza tym są to tylko przykłady:

    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 the child / services / wywołania timera:

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

Jeśli uruchomisz detectChanges ręcznie dodaj również logowanie:

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

Następnie w Debuggerze Chrome po prostu filtruj przez 'EXPRESSIONCHANGES'. Pokaże Ci dokładnie przepływ i kolejność wszystkiego, co zostanie ustawione, a także dokładnie w jakim punkcie kątowym rzuca błąd.

Tutaj wpisz opis obrazka

Możesz również kliknąć szare linki, aby umieścić punkty przerwania.

Kolejna rzecz, na którą należy uważać, jeśli masz podobne właściwości w całym swoim aplikacja (taka jak style.background) Upewnij się, że debugujesz tę, którą myślisz , ustawiając ją na niejasną wartość koloru.

 0
Author: Simon_Weaver,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-02 21:19:58

Mój problem był oczywisty, kiedy dodałem *ngIf, ale to nie była przyczyna. Błąd był spowodowany zmianą modelu w znacznikach {{}}, a następnie próbą wyświetlenia zmienionego modelu w instrukcji *ngIf później. Oto przykład:

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

Aby Rozwiązać problem, zmieniłem miejsce, w którym wywołałem changeMyModelValue () na miejsce, które miało więcej sensu.

W mojej sytuacji chciałem changeMyModelValue() wywoływać zawsze, gdy komponent potomny zmieni dane. To wymagane tworzę i emituję Zdarzenie w komponent potomny, więc rodzic mógł go obsłużyć (przez wywołanie changeMyModelValue (). zobacz https://angular.io/guide/component-interaction#parent-listens-for-child-event

 0
Author: goku_da_master,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-11 18:50:50