ExpressionChangedAfterItHasBeenCheckederror Menjelaskan

Tolong jelaskan kepada saya mengapa saya terus mendapatkan kesalahan ini: ExpressionChangedAfterItHasBeenCheckederror: Ekspresi telah berubah setelah itu diperiksa.

Jelas, saya hanya mendapatkannya di dev mode, itu doesn't terjadi pada produksi saya membangun, tapi itu's sangat mengganggu dan saya hanya don't memahami manfaat dari memiliki kesalahan dalam dev lingkungan yang tidak't muncul di prod --mungkin karena kurangnya pemahaman.

Biasanya, cara untuk memperbaikinya cukup mudah, saya hanya membungkus menyebabkan kesalahan kode dalam setTimeout seperti ini:

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

Atau memaksa mendeteksi perubahan dengan sebuah konstruktor seperti ini: konstruktor(swasta cd: ChangeDetectorRef) {}:

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

Tapi kenapa aku terus-menerus mengalami kesalahan ini? Saya ingin memahami hal ini sehingga saya bisa menghindari ini hacky perbaikan di masa depan.

Mengomentari pertanyaan (1)

Aku punya masalah yang sama. Melihat siklus hidup kait dokumentasi, saya berubah ngAfterViewInit untuk ngAfterContentInit dan itu berhasil.

Komentar (3)

Kesalahan ini menunjukkan masalah nyata dalam aplikasi anda, oleh karena itu masuk akal untuk melempar sebuah exception.

Di devMode deteksi perubahan menambahkan tambahan berubah setelah setiap perubahan teratur deteksi yang dijalankan untuk memeriksa apakah model telah berubah.

Jika model telah berubah antara regular dan tambahan deteksi perubahan gilirannya, hal ini menunjukkan bahwa baik

  • deteksi perubahan itu sendiri telah menyebabkan perubahan
  • metode atau pengambil mengembalikan nilai yang berbeda setiap kali itu disebut

yang baik yang buruk, karena hal ini tidak jelas bagaimana untuk melanjutkan karena model mungkin tidak pernah stabil.

Jika Sudut berjalan deteksi perubahan sampai model stabil, ini bisa berjalan selamanya. Jika Sudut doesn't berjalan deteksi perubahan, maka pandangan ini mungkin tidak mencerminkan keadaan saat ini model.

Lihat juga https://stackoverflow.com/questions/34868810/what-is-difference-between-production-and-development-mode-in-angular2/34868896#34868896

Komentar (6)
Larutan

Banyak pemahaman yang datang setelah aku mengerti Sudut Siklus hidup Kait dan hubungan mereka dengan deteksi perubahan.

Saya mencoba untuk mendapatkan Sudut untuk memperbarui global bendera yang terikat ke *ngIf dari sebuah elemen, dan saya mencoba untuk mengubah bendera itu dalam ngOnInit() siklus hidup hook dari komponen lain.

Menurut dokumentasi, metode ini disebut setelah Sudut sudah terdeteksi perubahan:

Disebut sekali, setelah yang pertama ngOnChanges().

Jadi memperbarui bendera dalam ngOnChanges() tidak't melakukan deteksi perubahan. Kemudian, setelah deteksi perubahan secara alami dipicu lagi, bendera's nilai telah berubah dan kesalahan dilemparkan.

Dalam kasus saya, saya berubah ini:

constructor(private globalEventsService: GlobalEventsService) {

}

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

Untuk ini:

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

ngOnInit() {

}

dan itu tetap masalah :)

Komentar (2)

Update

Saya sangat merekomendasikan dimulai dengan OP's self respon pertama: benar berpikir tentang apa yang bisa dilakukan di pembina lebih baik dari apa yang harus dilakukan di ngOnChanges().

Asli

Ini adalah sebuah catatan dari sebuah jawaban, tapi ini mungkin bisa membantu seseorang. Aku tersandung pada masalah ini ketika mencoba untuk membuat kehadiran tombol tergantung pada keadaan dari bentuk:

Yo

Sejauh yang saya tahu, sintaks ini mengarah ke tombol yang ditambahkan dan dihapus dari DOM yang didasarkan pada kondisi. Yang pada gilirannya mengarah ke ExpressionChangedAfterItHasBeenCheckederror.

Perbaikan dalam kasus saya (meskipun saya don't mengklaim untuk memahami implikasi penuh dari perbedaan), adalah menggunakan display: none sebagai gantinya:

Yo
Komentar (5)

Ada jawaban yang menarik tapi saya tidak't tampaknya menemukan satu yang sesuai dengan kebutuhan saya, yang paling dekat dari @chittrang-mishra yang mengacu hanya untuk satu fungsi tertentu dan tidak beberapa matikan seperti dalam aplikasi saya.

Saya tidak ingin menggunakan [hidden] untuk mengambil keuntungan dari *ngIf bahkan tidak menjadi bagian dari DOM jadi saya menemukan berikut solusi yang mungkin tidak menjadi yang terbaik untuk semua karena menekan kesalahan bukan mengoreksi, tapi dalam kasus saya di mana saya tahu hasil akhir adalah benar, tampaknya ok untuk aplikasi saya.

Apa yang saya lakukan adalah menerapkan AfterViewChecked, tambahkan konstruktor(pribadi changeDetector : ChangeDetectorRef ) {} dan kemudian

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

Saya harap ini membantu lain seperti banyak orang lain telah membantu saya.

Komentar (1)

Dalam kasus saya, saya punya masalah ini saya spec file, saat menjalankan tes saya.

Saya harus mengubah ngIf untuk [hidden]

untuk

Komentar (3)

Ikuti langkah-langkah di bawah ini:

  1. Gunakan 'ChangeDetectorRef' dengan mengimpor dari @sudut/inti sebagai berikut:
import{ ChangeDetectorRef } from '@angular/core';
  1. Menerapkannya dalam konstruktor() sebagai berikut:
constructor(   private cdRef : ChangeDetectorRef  ) {}
  1. Tambahkan metode berikut untuk fungsi yang anda panggil di acara seperti klik tombol. Sehingga terlihat seperti ini:
functionName() {   
    yourCode;  
    //add this line to get rid of the error  
    this.cdRef.detectChanges();     
}
Komentar (0)

Saya menghadapi masalah yang sama seperti nilai yang berubah di salah satu hotel di komponen. Tapi bukan mendeteksi perubahan pada perubahan nilai, saya mengubah komponen deteksi perubahan strategi untuk onPush (yang akan mendeteksi perubahan pada perubahan objek dan bukan pada nilai perubahan).

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

@Component({
    changeDetection: ChangeDetectionStrategy.OnPush
    selector: -
    ......
})
Komentar (3)

Sudut berjalan deteksi perubahan dan ketika menemukan bahwa beberapa nilai-nilai yang telah diwariskan kepada anak komponen telah berubah Sudut melempar kesalahan:

ExpressionChangedAfterItHasBeenCheckederror klik untuk lebih

Dalam rangka untuk memperbaiki ini, kita dapat menggunakan AfterContentChecked siklus hidup hook dan

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

  constructor(
  private cdref: ChangeDetectorRef) { }

  ngAfterContentChecked() {

    this.cdref.detectChanges();

  }
Komentar (3)

Mengacu pada artikel https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4

Jadi mekanisme di balik deteksi perubahan benar-benar bekerja dengan cara yang kedua deteksi perubahan dan verifikasi mencerna dilakukan serentak. Itu berarti, jika kita update sifat asynchronously nilai-nilai tersebut tidak akan diperbarui ketika verifikasi loop yang sedang berjalan dan kita tidak akan mendapatkan ExpressionChanged... kesalahan. Alasan kita mendapatkan kesalahan ini, selama proses verifikasi, Sudut melihat nilai-nilai yang berbeda maka apa yang tercatat selama perubahan fase deteksi. Jadi untuk menghindari itu....

  1. Menggunakan changeDetectorRef

  2. menggunakan setTimeOut. Ini akan mengeksekusi kode anda di VM lain sebagai makro-tugas. Sudut tidak akan melihat perubahan-perubahan selama proses verifikasi dan anda tidak akan mendapatkan kesalahan itu.

 setTimeout(() => {
        this.isLoading = true;
    });
  1. Jika anda benar-benar ingin mengeksekusi kode yang sama pada VM menggunakan seperti
Promise.resolve(null).then(() => this.isLoading = true);

Ini akan membuat mikro-tugas. Mikro-tugas antrian diproses setelah saat sinkron kode yang telah selesai melaksanakan karenanya update ke properti yang akan terjadi setelah langkah-langkah untuk verifikasi.

Komentar (2)

@HostBinding dapat membingungkan sumber kesalahan ini.

Sebagai contoh, katakanlah anda memiliki host berikut mengikat dalam komponen

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

Untuk kesederhanaan, katakanlah properti ini diperbarui melalui input berikut properti:

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

Di induk komponen anda programatik pengaturan dalam ngAfterViewInit

@ViewChild(ImageCarousel) carousel: ImageCarousel;

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

Berikut ini's apa yang terjadi :

  • Orang tua adalah komponen yang dibuat
  • Yang ImageCarousel komponen yang dibuat, dan ditugaskan untuk carousel (via ViewChild)
  • Kita dapat't access carousel sampai ngAfterViewInit() (itu akan menjadi null)
  • Kita menetapkan konfigurasi, yang menetapkan style_groupBG = 'merah'
  • Hal ini pada gilirannya set latar belakang: merah pada host ImageCarousel komponen
  • Komponen ini adalah 'dimiliki' oleh orang tua komponen, sehingga ketika memeriksa perubahan yang ditemukan perubahan pada korsel.gaya.latar belakang dan isn't cukup pintar untuk tahu bahwa ini isn't masalah sehingga melempar pengecualian.

Salah satu solusinya adalah untuk memperkenalkan lain wrapper div insider ImageCarousel dan mengatur warna latar belakang itu, tapi kemudian anda don't mendapatkan beberapa manfaat dari menggunakan HostBinding (seperti memungkinkan orang tua untuk mengontrol penuh-batas objek).

Solusi yang lebih baik, orangtua komponen untuk menambahkan detectChanges() setelah pengaturan konfigurasi.

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

Ini mungkin terlihat cukup jelas diatur seperti ini, dan sangat mirip dengan jawaban yang lain tapi ada's perbedaan yang halus.

Pertimbangkan kasus di mana anda don't menambahkan @HostBinding sampai nanti selama pengembangan. Tiba-tiba anda mendapatkan pesan kesalahan ini dan itu doesn't tampaknya masuk akal.

Komentar (0)

Solusi yang bekerja untuk saya menggunakan rxjs ``naskah impor { mulaidengan, ketuk, delay } dari 'rxjs/operator';

// Data field yang digunakan untuk mengisi pada html dataSource: ada;

....

ngAfterViewInit() { ini.yourAsyncData. .pipa( mulaidengan(null), delay(0), tekan((res) => ini.dataSource = res) ).berlangganan(); } ``

Komentar (0)

Debugging tips

Kesalahan ini bisa sangat membingungkan, dan itu's mudah untuk membuat asumsi yang salah tentang kapan tepatnya itu's terjadi. Saya menemukan itu berguna untuk menambahkan banyak debugging pernyataan seperti ini, semua komponen yang terkena dampak di tempat yang tepat. Ini membantu memahami alur.

Orangtua menempatkan pernyataan-pernyataan seperti ini (string yang tepat 'EXPRESSIONCHANGED' ini penting), tapi lain dari itu ini adalah hanya contoh:

    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');

Pada anak / jasa / timer callback:

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

Jika anda menjalankan detectChanges secara manual menambahkan penebangan untuk itu juga:

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

Kemudian di Chrome debugger hanya filter oleh 'EXPRESSIONCHANGES'. Ini akan menunjukkan anda persis aliran dan ketertiban dari segala sesuatu yang akan diatur, dan juga persis pada titik Sudut melempar kesalahan.

Anda juga dapat klik pada tautan abu-abu untuk menempatkan breakpoints di.

Hal lain yang harus diperhatikan jika anda bernama sama sifat semua aplikasi anda (seperti gaya.latar belakang) pastikan anda're debugging salah satu yang anda pikir anda - dengan pengaturan itu untuk mengaburkan nilai warna.

Komentar (0)

Masalah saya itu terwujud ketika saya menambahkan *ngIf tapi itu bukan't penyebabnya. Kesalahan itu disebabkan oleh perubahan model di {{}} kategori kemudian mencoba untuk menampilkan berubah model di *ngIf pernyataan di kemudian hari. Berikut ini's contoh:

<div>{{changeMyModelValue()}}</div> 
....
<div *ngIf="true">{{myModel.value}}</div>

Untuk memperbaiki masalah ini, saya berubah di mana saya disebut changeMyModelValue() ke tempat yang lebih masuk akal.

Dalam situasi saya aku ingin changeMyModelValue() disebut setiap kali seorang anak komponen mengubah data. Ini diperlukan saya buat dan memancarkan sebuah acara di komponen anak sehingga orang tua bisa mengatasinya (dengan menyebut changeMyModelValue(). lihat https://angular.io/guide/component-interaction#parent-listens-for-child-event

Komentar (0)

Untuk masalah saya, saya membaca github - "ExpressionChangedAfterItHasBeenCheckederror ketika mengubah komponen 'bebas model' nilai dalam afterViewInit" dan memutuskan untuk menambahkan ngModel

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

Tetap masalah saya, saya berharap hal ini membantu seseorang.

Komentar (2)

Aku punya semacam ini kesalahan dalam Ionic3 (yang menggunakan Sudut 4 sebagai bagian dari it's technology stack).

Bagi saya itu melakukan ini:

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

Jadi saya mencoba untuk kondisional mengubah jenis ion-icon dari pin untuk menghapus lingkaran, per modus layar itu beroperasi pada.

I'm menebak I'll harus menambahkan *ngIf sebagai gantinya.

Komentar (0)

Dalam kasus saya, saya telah async properti di LoadingService dengan BehavioralSubject isLoading

Menggunakan [tersembunyi] model karya, tapi *ngIf gagal

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

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

Berikut ini's pikiran saya pada apa yang terjadi. Saya belum membaca dokumentasi tapi saya yakin ini adalah bagian dari mengapa kesalahan ditampilkan.

*ngIf="isProcessing()" 

Ketika menggunakan *ngIf, secara fisik mengubah DOM dengan menambahkan atau menghapus elemen setiap kali perubahan kondisi. Jadi jika perubahan kondisi sebelum diberikan ke tampilan (yang sangat mungkin terjadi pada Sudut's world), kesalahan dilemparkan. Lihat penjelasan di sini antara pengembangan dan produksi mode.

[hidden]="isProcessing()"

Ketika menggunakan [hidden] tidak secara fisik mengubah DOM tetapi hanya menyembunyikan elemen dari pandangan, kemungkinan besar menggunakan CSS di belakang. Unsur ini masih ada dalam DOM, tetapi tidak terlihat tergantung pada kondisi's nilai. Itulah sebabnya kesalahan tidak akan terjadi bila menggunakan [hidden].

Komentar (1)

Saya harap ini membantu seseorang datang ke sini: Kami membuat layanan panggilan di ngOnInit dengan cara berikut dan menggunakan variabel displayMain untuk mengontrol Pemasangan elemen DOM.

komponen.ts

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

dan component.html

<div *ngIf="displayMain"> 

</div>
Komentar (0)

Aku punya kesalahan ini karena saya menggunakan variabel dalam component.html yang tidak dinyatakan dalam komponen.ts. Setelah saya dihapus bagian dalam HTML, kesalahan ini sudah pergi.

Komentar (0)