[ ] Regression (behavior that used to work and stopped working in a new release)
[X ] Bug report #14748
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
Setelah menggunakan objek yang dapat diamati di template saya menggunakan async, saya menerima:
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Ekspresi telah berubah setelah diperiksa. Nilai sebelumnya: ''. Nilai saat ini: 'sesuatu'
<br i="15"/>
Angular version: 4.2.2
<p i="16">Browser:</p>
<ul i="17">
<li i="18">[X] Chrome (desktop) version Version 58.0.3029.110 (64-bit)</li>
</ul>
<p i="19">For Tooling issues:</p>
<ul i="20">
<li i="21">Node version: v6.10.3</li>
<li i="22">Platform: Mac</li>
</ul>
Punya masalah yang sama dengan menggunakan redux. Ini adalah tautan ke masalah yang saya posting di repositori mereka. Ketika pada sudut 2 tidak ada kesalahan tetapi pada sudut 4.2.2 saya mendapatkan ekspresi berubah setelah itu diperiksa kesalahannya.
https://github.com/angular-redux/store/issues/428
Masalahnya muncul pada saya pada komponen saya saat beralih dari 4.1.3 ke 4.2.x. Mengembalikan ke 4.1.3 memperbaiki masalah bagi saya, tetapi itu bukan sesuatu yang ingin saya simpan selamanya.
Saya dapat mereproduksi pada proyek minimal, saya masih mencoba mencari cara untuk memperbaiki regresi (mungkin) ini. Tapi sepertinya pemeriksaan kotor telah diterapkan di dev. mode dari 4.2.x. Tapi changelog tidak jelas tentang itu. Tip apapun diterima.
Saya mendapatkan masalah yang sama setelah memutakhirkan dari 4.1.3 ke 4.2.3. Menggunakan setTimeout memperbaiki masalah.
dari:
PanictUtil.getRequestObservable (). Subscribe (data => this.requesting = data);
untuk:
PanictUtil.getRequestObservable (). Subscribe (data => setTimeout (() => this.requesting = data, 0));
@jingglang Bisakah Anda mencoba mereproduksi masalah di http://plnkr.co/edit/tpl : AvJOMERrnz94ekVua0u5 dengan kode seminimal mungkin?
Saya mendapatkan masalah yang sama - ditingkatkan dari 2.4 ke 4.2, dan beberapa logika sembunyikan / tampilkan saya sekarang rusak. Ini adalah logika gaya akordeon, di mana visibilitas bergantung pada perbandingan panel saat ini dengan yang dapat diamati yang menahan panel mana yang harus terlihat (antara lain). Komponen menggunakan @select untuk mendapatkan observable, dan itu terikat dengan | async di template - berfungsi seperti pesona di 2.4, sekarang dapatkan ExpressionChangedAfterItHasBeenCheckedError dan panel tidak bersembunyi dan ditampilkan pada klik pertama, hanya pada yang kedua.
Saya mendapatkan masalah yang sama saat memperbarui 4.1.3 ke 4.2.2.
Tapi, saya menemukan solusi.
Injeksi ChangeDetectionRef
, dan panggil fungsi detectionChanges()
pada titik kesalahan.
constructor(private cdr: ChangeDetectionRef) {
}
onChange(): void {
this.val += 1; // <- get error after
this.cdr.detectionChanges();
}
Saya rasa ini adalah masalah dengan perubahan mengikuti yang dibuat di 4.2.
https://github.com/angular/angular/pull/16592
untuk mengatasi masalah ini: https://github.com/angular/angular/issues/14321
Apakah Anda memiliki konten di dalam tag ng-content
?
Saya memiliki masalah yang sama dengan formulir di dalam tag ng-content
yang sekarang mengalami kesalahan dengan kesalahan ContentChangedAfter.
Saya mendapatkan masalah yang sama saat memperbarui 4.1.3 ke 4.2.0 atau lebih tinggi
Saya melampirkan proyek kecil untuk mereproduksi bug
Menindaklanjuti, saya menurunkan versi ke 4.1.3 (seperti yang disarankan di atas) dan kesalahan hilang, jadi apa pun konfliknya, ini khusus untuk 4.2. Saya tidak punya waktu untuk menyusun plunkr minggu ini (atau berikutnya), tetapi tampaknya ini terkait dengan satu tindakan yang memperbarui dua observable terpisah di toko, yang masing-masing berdampak pada UI. Salah satu pembaruan UI terjadi, yang lainnya menyebabkan pengecualian.
Hmmm,
Kembalikan ke versi 4.1.3 pasti akan menyelesaikan masalah dan juga menerapkan batas waktu seperti yang ditunjukkan oleh @jingglang.
hai @tyts. Saya membuat http://plnkr.co/edit/XAxNoV5UcEJOvsAbeLHT untuk masalah ini. semoga membantu.
solusi yang diberikan oleh @jingglang berfungsi (untuk saya) di 4.2.0 atau lebih tinggi
ATAU juga mengubah acara pemancar anak menjadi konstruktornya tidak menimbulkan kesalahan lagi
@umens Anda memperbarui komponen induk setelah diperiksa sehingga Anda tidak menyimpan aliran data searah
@ alexzuza bagaimana saya bisa mencapai itu? Saya selalu melakukannya dengan cara ini tanpa kesalahan. Mengapa menempatkan SetTimout tidak menimbulkan kesalahan lagi. (Saya memperbarui plunkr) Saya ragu itu mengganggu siklus hidup atau apakah itu?
hai @umens terima kasih atas waktunya. Masalahnya adalah apa yang dikatakan @alexzuza : Anda memperbarui bidang komponen induk setelah dicentang. Kode Anda kira-kira sama dengan ini: http://plnkr.co/edit/ksOdACtXScZKw3VRAYTm?p=preview (perhatikan saya menghapus layanan, untuk mengurangi kode).
Mengapa ini berhasil? Mungkin secara tidak sengaja versi lama sudut atau Rxjs memiliki bug di sini. Bisakah Anda menjelaskan versi mana yang digunakan? Atau bahkan dimasukkan ke dalam plunker yang berfungsi?
Mengapa menempatkan SetTimout tidak menimbulkan kesalahan lagi.
Karena Anda mengubah bidang secara asinkron, yang mengikuti aliran data satu arah.
Mari kita pertimbangkan dari sebaliknya: Mengapa kesalahan dimunculkan? Pemeriksaan pertama Deteksi Perubahan dimulai dari atas ke bawah. Ia memeriksa app.toolsConfig
nilai terikat ke template. Kemudian merender <child-cmp>
yang secara sinkron memperbarui app.toolsConfig
. Rendering selesai. Sekarang menjalankan pemeriksaan kedua (hanya mode dev) dari Deteksi Perubahan untuk memastikan bahwa status aplikasi stabil. Tapi app.toolsConfig
sebelum merender anak tidak sama dengan app.toolsConfig
setelahnya. Semua ini terjadi selama "putaran", "siklus" Deteksi Perubahan.
baik. terima kasih atas jawaban rinci. Saya tidak dapat menemukan informasi tentang kapan siklus hidup komponen anak terjadi.
untuk versi "berfungsi" sebelumnya yang saya gunakan:
@ angular / *: 4.1.1 (kecuali compiler-cli -> 4.1.0)
rxjs: 5.3.1
@umens di sini adalah kesalahan yang direproduksi dengan versi lama yang Anda sebutkan: http://plnkr.co/edit/kfoKmigXzFXwOGb2wyT1?p=preview. Ini melempar, jadi mungkin beberapa ketergantungan pihak ke-3 mempengaruhi perilaku atau instruksi reproduksi tidak lengkap?
tidak tahu.
ini yarn.lock saya jika Anda tertarik untuk melangkah lebih jauh: https://pastebin.com/msARLta1
tetapi saya tidak tahu apa yang bisa berbeda
Saya melihat kesalahan serupa "ExpressionChanged ..." setelah beralih dari 4.1.2
ke 4.2.3
.
Dalam kasus saya, saya memiliki dua komponen yang berinteraksi melalui layanan. Layanan ini disediakan melalui modul utama saya, dan ComponentA
dibuat sebelum ComponentB
.
4.1.2
, sintaks berikut bekerja dengan baik tanpa kesalahan:Template KomponenA:
<ul *ngIf="myService.showList">
...
Kelas KomponenB:
ngOnInit() {
this.myService.showList = false; //starts as true in the service
...
}
4.2.3
, saya harus memodifikasi template ComponentA
sebagai berikut untuk menghindari kesalahan "ExpressionChanged ...":Template KomponenA:
<ul [hidden]="!myService.showList">
...
Saya masih mencoba mencari tahu mengapa ini baru saja menjadi masalah, dan mengapa peralihan dari *ngIf
menjadi [hidden]
berfungsi.
Saya memiliki masalah yang sama, dan itu terjadi sebagian besar ketika saya menggunakan pipa asinkron seperti ini:
<div>{{ (obs$ |async).someProperty }}</div>
Jika saya menggunakannya seperti ini:
<div *ngIf="obs$ | async as o">
<div>{{ o.someProperty }}</div>
</div>
Kemudian kesalahan tersebut hilang, jadi saya tidak begitu yakin itu hanya karena banyak orang membuat kesalahan yang tidak terdeteksi sebelumnya: Saya pikir ada bug yang diperkenalkan di 4.2…
Masalah yang sama bagi saya. Masalahnya sepertinya berasal dari @ angular / router
Saya juga sampai pada kesimpulan itu terkait dengan router. Sudah mencoba memposting sebanyak kemarin, tetapi sepertinya postingan tersebut gagal. Saya menyelidiki lebih lanjut dan menyederhanakan kode untuk melihat apakah saya dapat melacak kesalahan tersebut. Kode berikut berfungsi dengan sempurna ketika sumber utama tindakan adalah klik pada header panel, tetapi gagal dengan kesalahan ExpressionChanged saat dipicu melalui perutean.
<span class="glyphicon pull-right"
[ngClass]="{'glyphicon-chevron-up' : (ui$ | async)?.visiblePanels[VisiblePanel.IngredientTypes], 'glyphicon-chevron-down' : !((ui$ | async)?.visiblePanels[VisiblePanel.IngredientTypes])}"></span>
Ini membantu saya memecahkan masalah! Anda harus secara eksplisit memicu perubahan pada induknya.
Seperti @acidghost , saya dapat mengatasi masalah ini dengan menjalankan ChangeDetectorRef#detectChanges
di AfterViewInit
dari komponen induk saya.
Jika Anda memiliki plunkr repro, saya bersedia memeriksanya karena kami memiliki beberapa keluhan serupa tentang gitter dan selalu berakhir menjadi masalah pengguna.
Tidak dapat memastikan apakah bug diperkenalkan di 4.2 atau jika bug itu lebih merupakan kurangnya kesalahan di <4.2 dan diselesaikan di 4.2.
Kesalahan: ExpressionChangedAfterItHasBeenCheckedError
larutan:
componentRef.changeDetectorRef.detectChanges ();
Di bawah ini adalah bagian dari kode saya (komponen yang dibuat secara dinamis):
componentRef = viewContainerRef.createComponent (componentFactory); // komponen dibuat
(componentRef.instance) .data = input_data; // mengubah data komponen secara manual di sini
componentRef.changeDetectorRef.detectChanges (); // itu akan menghasilkan deteksi perubahan
Untuk komponen sederhana, cukup google cara mengakses "componentRef" dan kemudian jalankan
componentRef.changeDetectorRef.detectChanges ();
setelah mengatur / mengubah data / model komponen.
Solusi 2:
Saya sekali lagi mendapatkan kesalahan ini saat membuka dialog "this.dialog.open (DialogComponent)".
jadi meletakkan kode dialog terbuka ini di dalam fungsi dan kemudian menerapkan setTimeout () pada fungsi itu menyelesaikan masalah.
ex:
openDialog () {
biarkan dialogRef = this.dialog.open (DialogComponent);
}
ngOnInit (): void {
setTimeout (() => this.openDialog (), 0);
}
meskipun sepertinya hack .. bekerja !!!
Sejak 4.2 saya memiliki masalah yang sama dan tidak ada solusi atau solusi yang berhasil untuk saya :( Saya membuat komponen secara dinamis dengan pabrik komponen dalam metode ngAfterViewInit. Komponen ini menggunakan arahan yang memiliki bidang @Input()
. Saya mengikat ini menjadi string statis. Tampaknya bidang @Input()
dari direktif tidak terdefinisi hingga tampilan komponen dibuat dan diperiksa dan baru kemudian diinisialisasi dengan string statis.
https://plnkr.co/edit/E7wBQXm8CVnYypuUrPZd?p=info
staticComponent.ts
export class StaticComponent implements AfterViewInit {
@ViewChild('dynamicContent', {read: ViewContainerRef})
dynamicContent: ViewContainerRef;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {}
ngAfterViewInit(): void {
const componentFactory: ComponentFactory<DynamicComponent> = this.componentFactoryResolver.resolveComponentFactory(DynamicComponent);
this.dynamicContent.createComponent(componentFactory);
}
}
test.directive.ts
@Directive({
selector: '[testDirective]'
})
export class TestDirective {
@Input()
testDirective: string;
}
dynamic.component.ts
@Component({
selector: 'dynamic-component',
template: `<div [testDirective]="'test'">XXX</div>`
})
export class DynamicComponent {
}
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Ekspresi telah berubah setelah diperiksa. Nilai sebelumnya: 'undefined'. Nilai saat ini: 'test'. Sepertinya tampilan telah dibuat setelah induk dan turunannya diperiksa kotor. Apakah itu telah dibuat dalam hook deteksi perubahan?
Saya agak bingung membaca utas ini. Apakah ini bug yang diketahui, yang akan diselesaikan dalam rilis mendatang? Atau, apakah ini perilaku yang diharapkan? Jika ya, apa solusinya? Terima kasih.
Tidak tahu apakah itu terkait atau tidak, tapi saya benci pesan ini dengan penuh semangat! ini HANYA muncul untuk saya saat unit testing dengan perintah 'ng test' di luar kotak. Saya kira saya bisa saja bingung tentang cara berhasil mengejek layanan sederhana. Juga terkejut tidak ada orang lain yang melihat ini dalam pengujian, tidak dapat menemukan pengujian apa pun yang terkait. Saya akan mengamati topik ini dan setiap penelusuran untuk pengujian unit ExpressionChangedAfterItHasBeenCheckedError +.
Saya telah mencoba untuk menyusun sebuah plnkr, dan merasa sangat sulit untuk mereproduksi kesalahan tersebut - namun, debugging yang ekstensif, saya pikir, membuat saya sedikit lebih dekat untuk melihat apa yang salah (meskipun tidak memutuskan apakah itu bug, atau kesalahan saya).
Saya membuat aplikasi sampel kecil menggunakan versi pengurangan dari reduksi yang sama yang digabungkan dalam urutan yang sama seperti di aplikasi nyata, dengan routerReducer terakhir. Yang membuat saya frustrasi, kesalahan itu tidak terjadi. Jadi saya menetapkan breakpoint dan mengamati aliran tindakan melalui reduksi. Apa yang saya temukan adalah dua aplikasi mengirimkan tindakan dalam urutan terbalik.
Peredam UI saya mengelola visibilitas, dan merupakan sumber perubahan status yang memicu error. Berikut adalah versi pengurangan peredam, yang menunjukkan tindakan yang relevan:
export const userInterfaceReducer = (state = INITIAL_UI(), action: any) => {
switch (action.type) {
case IngredientActions.LIST_INGREDIENTS:
if (action.hideTypes) {
return Object.assign(state, { visiblePanel: VisiblePanel.Ingredients });
}
return state;
default:
return state;
}
}
Saat saya menjalankan ini di aplikasi pengujian kecil saya, ini berfungsi - pertama, tindakan LIST_INGREDIENTS diaktifkan dan status baru dikembalikan; kemudian tindakan @ angular-redux / router :: UPDATE_LOCATION diaktifkan, dan kasus default dipukul, mengembalikan status yang sama.
Saat saya menjalankannya di aplikasi sebenarnya, urutan atau tindakannya dibalik. @ angular-redux / router :: UPDATE_LOCATION diaktifkan terlebih dahulu, mengembalikan status lama dari default. Kemudian LIST_INGREDIENTS diaktifkan, mengembalikan status baru (diubah) - dan kesalahan dimunculkan.
Saya sepenuhnya terbuka dengan gagasan bahwa saya telah melakukan sesuatu yang bodoh dan ini bukan bug yang sebenarnya - tetapi jika demikian, apakah ada orang lain yang melihat apa yang telah saya lakukan?
(Sebagai catatan kaki, saya memeriksa versi 4.1.3 ... dan itu juga mengaktifkan reduksi redux dalam urutan 'benar' - yaitu, perubahan lokasi diaktifkan setelah tindakan bahan daftar, yang mungkin itulah mengapa berhasil, tetapi versi 4.2 tidak).
Menderita bug mengganggu yang sama ketika salah satu komponen mengubah properti layanan. Diselesaikan dengan memindahkan * ngIf ke komponen induk. Jadi ini pasti bug
mampu mengatasi ini dengan menggunakan metode ngDoCheck () dan memanggil metode ChangeDetectorRef.detectChanges.
public ngDoCheck(): void {
this.cdr.detectChanges();
}
@pappacurds Jangan lakukan itu. Anda telah menyebabkan siklus Deteksi Perubahan tak terbatas dengan itu.
mungkin kesalahan yang sama di sini https://plnkr.co/edit/IeHrTX0qil17JK3P4GBb?p=preview
kesalahan: previous undefined after undefiend
setelah memperbarui kesalahan yang sama
Sama untuk ku
Semuanya baik-baik saja di 4.0.0, setelah memutakhirkan ke 4.2.6 saya mendapatkan kesalahan yang sama.
Artikel Semua yang perlu Anda ketahui tentang kesalahan ExpressionChangedAfterItHasBeenCheckedError
menjelaskan secara mendalam mengapa kesalahan ini terjadi dan kemungkinan perbaikan
perbaikan sudah jelas, tetapi mengubah kerangka kerja sedemikian rupa sehingga pembaruan menyebabkan aplikasi pengguna harus ditulis ulang untuk "mendukung" pembaruan platform minor baru adalah kabel. JS masih mempertahankan kesalahan lama untuk mempertahankan kompatibilitas ...
Kesalahan yang sama di sini
Saya juga melihat masalah ini di ~4.2.4
. Menurunkan versi ke ~4.1.3
menyelesaikan masalah.
Masalahnya adalah dengan mengubah nilai ContentChildren
, bahkan saat menggunakan ChangeDetectorRef
detectChanges()
pada akhir AfterViewInit
induk (di mana ia membuat perubahan). Anehnya masalah ini hanya terjadi dengan komponen dan bukan arahan. Saya mengubah arahan yang saya kerjakan di 4.2.4 menjadi komponen dengan template <ng-content></ng-content>
dan kemudian mulai membuang kesalahan ExpressionChangedAfterItHasBeenCheckedError
.
Saya mendapatkan ini saat bekerja dengan kontrol input materi 2. Mencoba untuk mengatur nilainya
ngAfterViewInit(): void {
this.numberFormControl.setValue(200);
}
Saya telah mengumpulkan Plunker sederhana yang menunjukkan kasus di mana kesalahan ExpressionChangedAfterItHasBeenCheckedError
hanya mulai muncul dimulai dengan Angular 4.2.x
.
Saya telah membuat plunker sederhana yang menunjukkan kesalahan saat mengubah nilai ContentChild
bahkan saat menggunakan ChangeDetectorRef detectChanges()
dalam komponen induk ngAfterViewInit
.
Sunting: Juga dibuat plunker sederhana berdasarkan contoh pertama saya tetapi dengan Petunjuk orang tua, bukan Komponen dan tidak ada kesalahan.
@saverett Saya melihat pesan kesalahan yang berbeda dengan plunk Anda
@JSMike Saya memiliki kasus yang persis sama seperti di plunker Anda (mengatasinya dengan membungkus kode yang merujuk ContentChild
dalam setTimeout
, plunker )
@JSMike Terima kasih atas perhatiannya. Sepertinya ada masalah dengan angular / zone.js # 832. Saya telah menetapkan versi zone.js ke 0.8.12 untuk saat ini sampai mereka memperbaiki zone.js. Plunker seharusnya bekerja kembali.
@ matkokvesic sebenarnya bukan solusi, yang hanya menyebabkan perubahan terjadi di luar pengait siklus proses. Contoh kode yang diberikan tidak menyebabkan error saat menggunakan Angular v4.1.3
@JSMike Saya setuju, Ini solusi sementara. Menggunakan elemen yang direferensikan oleh ContentChildren
di ngAfterViewInit
berfungsi dengan baik sebelum Angular 4.2.6.
Saya membuat Plunker untuk bug yang dideskripsikan di https://github.com/angular/angular/issues/17572#issuecomment -311589290
Saya telah memperbaikinya dengan menggunakan changeDetection: ChangeDetectionStrategy.OnPush
dan this.changeDetector.markForCheck();
dalam kode saya
@touqeershafi jika Anda menggunakan this.changeDetector.markForCheck();
bukan berarti Anda tidak menggunakan sesuatu dengan cara yang benar
Ini masih terjadi. Ini jelas bug, bukan perilaku yang diharapkan dan harus diperbaiki.
@istiti saya mengerti itu bukan cara yang tepat tetapi setidaknya Anda dapat menghilangkan kesalahan ini untuk sementara, Tapi sejauh menyangkut proyek saya, saya telah menambahkan TODO
dengan url bug.
Ini masih terjadi. Ini jelas bug, bukan perilaku yang diharapkan dan harus diperbaiki.
Saya rasa saya harus setuju dengan @rwngallego ... Sebelum pembaruan 4.2.x, saya sudah menulis sejumlah komponen stlye / ng-konten yang dapat digunakan kembali yang bergantung pada @ViewChildren , @ContentChildren dan membuat hubungan antara orang tua dan anak melalui ini saluran (mendapatkan / mengatur properti dan sebagainya).
Saya menemukan diri saya terus-menerus harus menyuntikkan contoh ChangeDetectorRef ke semua konstruktor ini (termasuk kelas anak yang memperluas komponen ini), dan itu tidak terasa seperti perilaku yang diinginkan atau benar.
Untuk yang menggunakan komponen dinamis seperti @saverett (menggunakan router menggunakan komponen dinamis) saya pikir masalahnya disebutkan di sini https://github.com/angular/angular/issues/15634.
Bagi siapa pun yang melakukan perubahan di dalam ngAfterViewInit
/ ngAfterViewChecked
saya pikir itu sudah diharapkan.
Bagi yang lain, (belum melihat satupun di sini) masih menjadi misteri bagi saya.
Ini terjadi pada saya dalam skenario berikut.
Memiliki komponen induk yang menggunakan komponen anak. Komponen anak membutuhkan data yang akan disediakan oleh induknya tetapi data ini berasal dari hasil promise di konstruktor induk. Begitu:
induk
constructor {
dataservice.getData().then((result) => {
this.source = result;
});
}
Template induk
<app-child [source]="source"></app-child>
komponen anak
@Input() source:any[];
Kode ini menampilkan kesalahan ini, namun menambahkan ini ke komponen induk menyelesaikan masalah:
constructor(private cdRef: ChangeDetectorRef) {
dataservice.getData().then((result) => {
this.source = result;
this.cdRef.detectChanges();
});
}
Sekarang apakah ini solusi yang direkomendasikan atau merupakan solusi untuk bug dan apa implikasi dari perubahan tersebut? bagi saya sepertinya Anda memberi tahu Angular untuk mendeteksi perubahan yang menurut saya adalah sesuatu yang harus Anda hindari jadi ...
@sulhome ... masalah Anda adalah bahwa promise dalam konstruktor induknya mengubah @Input
pada komponen anak ... selama siklus CD dan konfirmasinya dalam mode dev. Itulah alasan kesalahannya.
Baris this.cdRef.detectChanges();
menambahkan siklus CD kedua jadi konfirmasi setelah berhasil dan => tidak ada kesalahan.
Jadi semuanya berjalan seperti yang diharapkan. Penting untuk memahami cara kerjanya menggunakan konstruksi yang benar dan logika umum dalam kode Anda untuk menghindari jenis masalah ini. Anda dapat membacanya dalam penjelasan mendalam di sini: https://hackernoon.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
@ mlc-mlapis jadi Anda mengatakan bahwa saya melakukannya dengan benar dengan menambahkan this.cdRef.detectChanges();
dan ini tidak memiliki implikasi apa pun?
Saya berpikir dengan mendengarkan ngOnChanges
pada anak maka saya harus memilih perubahan di @Input dari induk. bukankah itu pendekatan yang tepat untuk melakukannya? Harap dicatat bahwa saya telah melakukan itu dan saya masih menerima kesalahan tetapi saya pikir untuk itulah ngOnChanges
dirancang
@sulhome Ya, this.cdRef.detectChanges();
adalah yang membantu tetapi juga memiliki konsekuensi bahwa Anda memiliki 2 CD sekarang ... jadi itu tergantung bagaimana dan di mana Anda menggunakan OnPush
strategi pada komponen untuk menghilangkan overhead satu siklus CD lagi.
Jika benar bahwa dataservice.getData
mengambil beberapa data menggunakan http maka mungkin solusi termudah adalah memiliki observable di layanan itu dan berlangganan dari komponen anak. Jadi ketika data akan dipancarkan di sungai, anak dapat bereaksi.
@ghetolay sebagai catatan, ada beberapa kasus ketika itu adalah bug, lihat contoh # 18129 yang saya buka.
Saya mengalami masalah ini ketika saya mendapatkan data dari komponen anak dengan @Output.
di komponen induk rumah
drag_second(trigger){
console.log("dragged222222"+trigger);
if(trigger){
this.isactive=true;
}else{
this.isactive=false;
}
}
dan home.html
<div class="upper" [ngClass]="{'isactive':isactive}">
isactive diubah saat dragstart dan dragend ....
pesan kesalahan adalah
ExpressionChangedAfterItHasBeenCheckedError: Ekspresi telah berubah setelah diperiksa. Nilai sebelumnya: 'true'. Nilai saat ini: 'false'.
atau adakah cara lain untuk mengubah / menambahkan atribut kelas?
Masalah yang sama di 4.2.x dan 4.3.0 tetapi tidak di 4.1.x.
Kesalahan hanya terjadi pada rute yang menggunakan ng-template
.
Solusi: Menempatkan semua kode di ngAfterViewInit
menjadi setTimeout
memecahkan masalah bagi saya (terima kasih / kredit untuk @jingglang).
public ngAfterViewInit(): void {
setTimeout(() => {
//my code
});
}
@Wernerson , lihat komentar JSMike tentang setTimeout. Penting untuk diperhatikan bahwa Anda sebenarnya hanya mengubah kode di luar pengait siklus proses.
Masalah saya adalah saya menggunakan ActivatedRoute
params
Observable
untuk mengubah beberapa keadaan di ngOnInit()
. Tampaknya, Router
memancarkan sinkronisasi params
, jadi pembaruan dilakukan selama CD.
Solusi sederhana saya untuk ini saat ini:
// !!! see delay(0)
// results are consumed by async Pipe, where I also had the CD issue (null vs. undefined)
this.results = this._activatedRoute.params.delay(0)
.do(params => this.searchParameters = this._createSearchParameters(params))
.switchMap(p => {
let key = this.searchParameters.key();
return this._searchResultCache.results.map(r => r.get(key));
});
(Saya tahu ini bukan kode reaktif / fungsional terindah yang pernah ada;))
Jadi solusinya adalah delay(0)
parameter emisi oleh satu tick
.
Semoga saya dapat membantu untuk orang lain yang memiliki masalah serupa.
Juga semua keprihatinan disambut, bagaimana solusi saya salah atau berbahaya.
Apa status masalah ini? Itu masih terlihat di Angular 4.2.6.
Apakah ini masalah dalam produksi build? Karena kesalahan ini hanya akan terjadi di debug build.
Saya benci semua solusi dengan menjalankan deteksi perubahan secara manual, atau bahkan menjalankannya di setTimeout () ...
setTimeout()
tidak efisien, karena menyebabkan siklus deteksi perubahan baru pada seluruh aplikasi, sedangkan detectChanges()
hanya memeriksa komponen saat ini AFAIK.
Tim saya memiliki kesalahpahaman inti tentang apa itu ngOnInit dan bukan. Kata dokumen
ngOnInit()
: Inisialisasi direktif / komponen setelah Angular pertama kali menampilkan properti terikat data dan menyetel properti masukan direktif / komponen. Dipanggil sekali, setelah ngOnChanges () pertama.
Kami berlangganan layanan kami (yang melacak status aplikasi) dari ngOnInit
. Langganan kami adalah BehaviorSubjects sinkron ; mereka memecat pada waktu berlangganan. Jadi "setelah Angular dulu ... menyetel properti input direktif / komponen", kami segera mengubah nilainya. Berikut adalah Plunker yang sangat sederhana yang menggambarkan bahwa menjadi masalah.
Contoh dalam Tour Of Heroes pada akhirnya menggunakan http yang bersifat asinkron , dan oleh karena itu tidak menimbulkan masalah. Saya pikir inilah sumber kebingungan kita.
Apa yang kami lakukan sekarang adalah berlangganan status sinkron kami dari konstruktor kami, sehingga nilai komponen kami akan segera disetel. Orang lain di utas ini menyarankan untuk memulai deteksi perubahan kedua melalui batas waktu, menyetel nilai nanti, atau secara eksplisit memberi tahu angular untuk melakukannya. Melakukannya akan mencegah kesalahan muncul, tetapi mungkin itu tidak perlu.
Artikel yang dibagikan @maximusk harus dibaca : Semua yang perlu Anda ketahui tentang kesalahan ExpressionChangedAfterItHasBeenCheckedError
.
Jika tidak ada yang lain, utas ini akan menghasilkan peringatan mendalam tentang semua ini di dokumen sudut resmi.
Artikel yang dibagikan @maximusk harus dibaca: Semua yang perlu Anda ketahui tentang kesalahan ExpressionChangedAfterItHasBeenCheckedError.
Terima kasih
@adil
Apakah ini berfungsi dalam kasus penggunaan @ViewChildren @ContentChildren untuk mengubah properti anak dari induknya, karena anak-anak tidak tersedia hingga siklus hidup ngAfterViewInit () telah dijalankan?
Harap diingat bahwa bagian dari masalah ini adalah bug yang dikonfirmasi dan bukan kesalahan pengguna:
https://github.com/angular/angular/issues/15634
Masalah yang dialamatkan @saverett benar, menggunakan *ngIf
menyebabkan kesalahan.
* menonton ...
Ini bisa terjadi jika Anda meneruskan objek ke dialog Material Angular dan kemudian dalam dialog menggunakannya secara langsung daripada melakukan this.myDataInput = Object.assign({}, dialogDataInput)
. Yaitu this.myDataInput = dialogDataInput;
dan kemudian melakukan sesuatu seperti this.myDataInput.isDefault = !this.myDataInput.isDefault;
. Seperti dijelaskan di atas, komponen anak (dialog) mengubah nilai yang diekspos dalam tampilan komponen induk.
ganti atribut dengan [atribut]
Adakah yang tahu pasti apakah ini adalah kesalahan atau bug yang diinginkan?
Bukankah benar memuat data di ngOnInit? Karena jika saya memindahkan kode ke konstruktor (memuat melalui Observable) kesalahan tidak terjadi ...
@Tokopedia . Apakah Anda memiliki * ngIf di html Anda? Berhati-hatilah karena segala sesuatunya akan dirender setelah ngOnInit
selesai, solong, semua yang ada di dalam * ngIf tidak berfungsi dan mungkin menyebabkan kesalahan ini.
Itu harus dikerjakan ulang. Dengan "ngIf" dan kontrol khusus Anda mendapatkan pesan kesalahan ini. Waktu tunggu tentu bukan solusi.
@ j-nord jika Anda membaca komentar, Anda akan tahu bahwa timeout
tidak diperlukan.
@ j-nord / @dgroh Saya pernah mengalami hal yang sama. Saya telah menggunakan petunjuk-ng yang menggunakan perintah ngIf dan saya tidak bisa menghilangkan kesalahan ini. Apakah Anda berhasil menerapkan solusi?
@Wernerson ya, sebagian dari masalah ini adalah kesalahan yang disengaja atau bug: https://github.com/angular/angular/issues/15634
@ mawo87 itu bekerja untuk saya dengan tersembunyi karena tidak menghapus elemen dari DOM. Untuk kebutuhan kami, ini tidak masalah dan juga merupakan solusi yang bersih.
@ muhammad.a
Pertama, Anda harus menemukan variabel yang dimaksud dengan pesan kesalahan tersebut. @ Tim : Akan menyenangkan untuk memiliki nama variabel dalam pesan kesalahan. Ini tidak mudah ditemukan oleh mask dan Webpack yang lebih besar. Jika Anda menemukan perintah di tempat itu terjadi, perintah di baris berikut akan membantu:
this.cdr.detectionChanges();
Lihat contoh dari vermilion928 dari 19. Juni.
Terima kasih @dgroh dan @ j-nord.
@ j-nord Saya juga melihat metode detectionChanges, tetapi saya mungkin tidak menggunakan pendekatan ini seperti pada artikel berikut ini disarankan untuk tidak melakukannya:
https://hackernoon.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
Dalam kasus saya, saya menggunakan layanan PageTitle dan berlangganan di komponen induk, dan kemudian ketika komponen anak dimuat ke dalam tampilan, mereka mengatur PageTitle dalam layanan dan induk mendapatkan nilai dan menampilkannya melalui langganan. Ini bekerja tanpa kesalahan apa pun di 4.1.x, tetapi memunculkan kesalahan ExpressionChangedAfterItHasBeenCheckedError di 4.2x dan yang lebih baru.
Semuanya masih berfungsi seperti yang diharapkan, dan kesalahan tidak ditampilkan dalam pembuatan yang diterapkan / produksi (karena kurangnya siklus deteksi perubahan kedua), tetapi dalam pengembangan ada banyak warna merah di konsol saya.
Saya masih sangat tidak jelas seperti apa perbaikan yang tepat (bahkan jangka pendek). Langganan komponen induk saya berada di konstruktor, dan pembaruan komponen anak melalui layanan berada di ngOnInit.
Saya telah membaca posting 'semua yang perlu Anda ketahui tentang ...', yang informatif, tetapi tidak membuat saya percaya bahwa saya melakukan sesuatu yang salah, dan tidak memberikan solusi yang berfungsi untuk masalah tersebut.
@alignsoft jika Anda akan mengatur pagetitle di komponen anak di ngOnInit, maka saya yakin kesalahan itu valid ... Lihat artikel yang ditautkan sebelumnya.
@alignsoft Saya setuju, ada banyak diskusi tentang kesalahan ini, tetapi tanpa instruksi yang jelas tentang apakah itu valid atau tidak ... apakah ini lebih merupakan "peringatan" untuk dev tentang standar dan data binding / manipulasi?
Dalam kasus khusus saya, saya berurusan dengan komponen induk / anak menggunakan @ViewChild dan @ContentChild , harus memanggil detectChanges () berulang kali untuk menghentikan kesalahan agar tidak muncul di konsol ...
@rolandoldengarm Saya sudah mencoba berlangganan di konstruktor induk dan menyetel nilai di AfterViewInit di komponen anak dan masih mendapatkan kesalahan. Apakah itu cara yang benar untuk menanganinya berdasarkan siklus deteksi aliran dan perubahan, dan kesalahan berpotensi menjadi bug?
Tolong, alangkah baiknya memiliki semacam jawaban resmi di sini ...
Lihat ini lagi. Sepertinya contoh saya dari https://github.com/angular/angular/issues/17572#issuecomment -315096085
seharusnya menggunakan AfterContentInit
daripada AfterViewInit
ketika mencoba memanipulasi ContentChildren sebagai bagian dari siklus hidup komponen.
Berikut plnkr yang diperbarui dengan AfterContentInit dan tidak ada lagi kesalahan: http://plnkr.co/edit/rkjTmqclHmqmpuwlD0Y9?p=preview
@JSMike Saya mengubah AfterViewInit
menjadi AfterContentInit
, masih kesalahan yang sama. Bahkan jika saya memperbarui data saya di dalam OnInit
kesalahan terjadi. Sejauh yang saya tahu ini sesuai dengan siklus hidup Angular benar (kan?) Untuk menginisialisasi data selama OnInit
. Bersama dengan fakta bahwa kesalahan ini tidak terjadi di versi sebelumnya, saya menganggap ini bug. Namun saya ingin mendapatkan semacam pernyataan dari tim pengembangan jika demikian ..? : D
@Wernerson Anda tidak boleh memperbarui data di dalam ngOnInit
kecuali data tersedia di hook siklus hidup itu. Anda harus memindahkan logika Anda menjadi ngAfterContentInit
jika ada hubungannya dengan konten anak-anak.
@JSMike Tidak membantu, kesalahan masih terjadi. Saya baru saja berlangganan perubahan status dalam aplikasi setelah saya menerima data yang diminta (secara asinkron) di ngAfterContentInit
.
@Wernerson apakah Anda memiliki contoh plnkr?
Sampel dasar dengan StackBlitz
Komponen anak yang memancarkan peristiwa dan komponen induk memperbarui variabel lokal.
Dapatkan kesalahan di konsol js:
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: 'hello world'.
Ketahui, ngAfterContentInit
hanya berfungsi jika anak-anak statis, masih gagal saat membuat anak menggunakan *ngFor
http://plnkr.co/edit/rkjTmqclHmqmpuwlD0Y9?p=preview
@soldiertt Anda hanya perlu detectChanges () https://stackblitz.com/edit/angular-qfu74y
@JSMike jika saya melakukan ini, ngAfterViewInit komponen anak tidak terpicu: https://stackblitz.com/edit/angular-bdwmgf
@soldiertt yang tampak aneh, tetapi tampaknya masalah terpisah yang harus dilaporkan.
@JSMike Pengaturan saya cukup rumit dengan layanan, pustaka pihak ketiga, informasi rahasia, dll. Sayangnya saya tidak dapat mereproduksi bug di plunker: /
Injeksi ChangeDetectorRef berhasil untuk saya!
Saya memiliki masalah ini di carousel, karena saya perlu menyetel beberapa properti pada komponen item carousel, properti yang hanya diketahui setelah dihitung dalam komponen carousel induk. Dan semua item masuk melalui ng-content
, jadi saya hanya dapat bekerja dengan item di ngAfterContentInit
komponen korsel induk. Sesuatu seperti:
korsel:
@ContentChildren(ItemComponent) carouselItems;
ngAfterContentInit() {
// ... some computations
this.carouselItems.forEach((item: ItemComponent, currentIndex) => {
item.setPropertyX(someComputedValue);
// OR:
item.x = someComputedValue; // of course, this would work as well
});
}
barang:
setPropertyX(value) {
this.x = value;
}
Selain peretasan setTimeout()
, apakah satu-satunya solusi lain adalah menggunakan layanan dan subjek agar item berkomunikasi dengan korsel induk? Maksud saya, itu konyol ... Artinya, jika saya memiliki 100 item di carousel, maka saya akan memiliki 100 langganan. Dan itu berlipat ganda jika saya memiliki 2 carousel. Tentunya itu tidak bisa menjadi cara yang direkomendasikan ?! Baik?
Lebih sering daripada tidak, kita perlu mengatur beberapa properti pada komponen kita, properti yang tidak dapat diketahui sebelum AfterContentInit
... Dan dalam banyak kasus (ketika kita tidak membicarakan tentang 1 atau 2 komponen seperti itu, melainkan 50 atau 100 seperti di carousel) memiliki persepuluhan atau ratusan langganan akan konyol.
Apakah ada bagian Angular yang sangat keren yang dapat membantu saya di sini, yang belum saya temukan?
NB Saya juga mencoba menyuntikkan ChangeDetectorRef
dan menggunakan detach()
pada komponen carousel dan komponen item. Saya mengharapkan ini untuk memperbaiki kesalahan karena, pada dasarnya saya mengatakan "deteksi perubahan _screw, saya sebenarnya tidak membutuhkannya sama sekali. Tolong, tolong: jangan mendeteksi perubahan APA PUN _" ... tapi kesalahannya masih terlempar! Jadi, apa gunanya metode detach()
, jika tidak melakukan apa yang dikatakannya?
constructor(private cdr: ChangeDetectorRef) {
this.cdr.detach();
}
setTimeout()
tidak perlu diretas, ini mungkin cara yang sangat valid untuk melakukan hal-hal seperti misalnya saat Anda bereaksi terhadap view untuk memperbaruinya, misalnya: mengubah binding template selama ngAfterViewInit
.
Masalahnya adalah ketika Anda menggunakannya sebagai sihir tanpa benar-benar mengetahui alasannya. Tetapi ada skenario yang benar-benar valid di mana menggunakan setTimeout()
adalah cara yang tepat.
@ Ghetolay Apakah tidak dievakuasi oleh sistem deteksi perubahan? Saya telah menggunakan rute ChangeDetectorRef.detectChanges (), tetapi itu bahkan terasa seperti hack bagi saya ... jika salah satu dari solusi ini "dapat diterima", saya kira kita harus menerimanya, tetapi itu benar-benar merusak penggunaan modifikasi ViewChild (ren) atau ContentChild (ren)
@bayu_joo
Apakah tidak hacky untuk keluar dari siklus hidup komponen yang dievakuasi oleh sistem deteksi perubahan?
Saya tidak mengerti pertanyaan itu. keluar dari siklus hidup?
Angular akan menjalankan putaran CD setiap kali terjadi peristiwa asinkron (acara dom, xhr, setTimeout dll ..), selama CD itu akan memperbarui tampilan. ngAfterViewInit
hook dijalankan setelah itu sehingga tidak ada lagi CD yang direncanakan untuk dijalankan dan tidak akan dijalankan lagi sampai event async lain terjadi. Menggunakan setTimeout()
adalah cara termudah untuk memicunya lagi. Anda pasti bisa melakukannya sendiri tapi menurut saya manfaatnya bisa diabaikan.
@ghetolay Saya mereferensikan komentar dari @JSMike dan beberapa utas lain yang telah saya baca:
@ matkokvesic sebenarnya bukan solusi, yang hanya menyebabkan perubahan terjadi di luar pengait siklus proses. Contoh kode yang diberikan tidak menyebabkan error saat menggunakan Angular v4.1.3
Jadi saya pikir menggunakan setTimeout () akan menyebabkan hook siklus hidup menjadi "dilewati" atau tidak dievaluasi seperti biasanya?
@ghetolay @fugwenna bagaimana dengan metode detach()
dari ChangeDetectorRef
? Ada pemikiran? Bukankah itu seharusnya menonaktifkan deteksi perubahan sepenuhnya, sehingga juga menghilangkan kesalahan? Saya tidak mengerti mengapa itu tidak berhasil? Atau apakah kesalahan itu dianggap "independen" dari itu? Artinya Angular tidak terlalu peduli dengan fakta bahwa kami telah menonaktifkan deteksi perubahan, hanya peduli bahwa kami telah mengubah properti setelah disetel?
Jika orang masih memperdebatkan tentang "ExpressionChangedAfterItHasBeenChecked" sebagai bug atau bukan, mungkin yang ini pasti bug ... Menurut pendapat saya, Angular tidak lagi peduli tentang kita mengubah properti setelah disetel, jika kita sudah sepenuhnya deteksi perubahan dinonaktifkan. Terutama cara saya melakukannya, untuk komponen induk dan semua komponen anak lainnya, pada dasarnya di seluruh pohon.
Saya agak tersesat di sini, salah satu masalah yang saya hadapi adalah saya tidak yakin bagaimana saya bahkan akan melakukan beberapa hal yang sangat umum tanpa memicu pengecualian dalam mode pengembang.
Misalnya saya telah menulis komponen pengakses nilai kontrol yang merangkum datepicker. Saya memberi makan komponen ini dengan model kosong yang diteruskan ke datepicker secara internal yang menginisialisasi dan mengirimkannya kembali melalui fungsi terdaftar. Tentu saja anak-anak memodifikasi model selama initnya, tapi itulah yang saya inginkan! Saya ingin mendapatkan kembali model yang diinisialisasi tetapi tidak melakukan inisialisasi pada induknya karena datepicker-lah yang memahami bagaimana melakukan itu.
Masalahnya adalah bagaimana saya melakukannya sekarang? Panggil setTimeout dan picu deteksi perubahan lagi? Gunakan ChangeDetectorRef? Saya tidak suka karena itu benar-benar membuat kode sulit dibaca. Inisialisasi sesuatu dalam layanan terpisah? Tetapi layanan itu hanya melayani tujuan yang sangat teknis tanpa nilai bisnis apa pun dan saya bahkan tidak yakin ini akan menyelesaikan masalah.
@fugwenna Perhatian utama saya adalah berhasil di 4.1.3 dan sekarang rusak. Saya belum melihat siapa pun di tim sudut berpadu tentang apakah kita harus menggunakan setTimeout
dalam fungsi init kita dan membiarkan zona mengambil alih, tetapi rasanya seperti anti-pola dibandingkan dengan cara kerjanya sebelumnya.
@ kavi87 Jadi pertanyaan stackoverflow ini benar-benar memberi saya solusi elegan yang saya butuhkan. Yang membuat event emitter di komponen anak saya async :
new EventEmitter(true)
Untuk masalah yang diberi label sebagai regresi dengan 109 komentar, masalah ini sangat mengecewakan dengan kata resmi apa pun.
Sementara semua orang di sini melakukan pekerjaan yang baik dengan teknik swadaya, saya ingin tahu apakah ini benar-benar bug, atau hanya beberapa perilaku baru yang diterapkan di 4.2.x dan seterusnya? Jika demikian, itu seharusnya terdaftar sebagai perubahan yang melanggar (dan dengan demikian bukan rilis kecil?)
@Tokopedia
Saya tidak yakin menonaktifkan deteksi perubahan sepenuhnya untuk membungkam pengecualian adalah jalan yang baik untuk turun, meskipun secara teori, saya melihat bagaimana itu seharusnya / bisa masuk akal.
Saya setuju dengan @JSMike dan @rosslavery sehubungan dengan sangat sedikit tanggapan atau penjelasan tentang mengapa pesan kesalahan ini muncul di posting 4.2x tanpa dokumentasi apa pun yang merusak perubahan ... namun bagian lain yang membingungkan dari ini adalah bagaimana tampaknya hanya dilemparkan mode pengembangan, yang hampir terasa seperti peringatan gangguan deteksi perubahan (?)
Im AppComponent saya, saya memiliki div.
Satu-satunya tujuan div itu adalah untuk menampilkan hamparan sibuk.
Status sibuk dipicu di dalam kelas layanan http:
app.component.html
<!-- Busy Overlay -->
<div *ngIf="busy$ | async"
class="overlay">
<i class="loading">
<i><sk-wave></sk-wave></i>
</i>
</div>
app.component.ts:
get busy$(): Observable<boolean> {
return this.catolService.busy$;
}
Setelah memutakhirkan ke 4.3.4 saya selalu mendapatkan ExpressionChangedAfterItHasBeenCheckedError saat layanan memicu status sibuk.
Apakah ada perbaikan untuk skenario asinkron sederhana ini?
@ emazv72 ... apakah demo stackblitz / plunker ini sama dengan kasus Anda? https://stackblitz.com/edit/angular-2p2h9s?file=app%2Fapp.component.ts atau https://plnkr.co/edit/RPOxSVnXvM1TqKp0peSY?p=preview
@mukunku Terima kasih, saya akan mencobanya.
@ mlc-mlapis Saya sedikit mengubah plunker Anda, kode saya persis https://plnkr.co/edit/uUT7tiM5BKPIy2upJPhb?p=preview
tapi plunker tidak menunjukkan masalahnya. Saya akan mencoba meningkatkan ke 4.x terbaru untuk melihat apakah sudah diperbaiki.
Terima kasih
Halo semuanya - maaf atas keterlambatannya di sini.
Jadi - perilaku ini sebenarnya bekerja sebagaimana mestinya, yang saya sadari mungkin tidak terduga bagi sebagian orang, jadi izinkan saya mencoba menjelaskan mekanismenya.
@saveretts 'plunker adalah repro yang bagus untuk berjalan melalui (terima kasih!) - https://plnkr.co/edit/fr7rTNTCgEhHcbKnxAWN?p=preview
Tldrnya adalah Angular selalu bekerja dengan cara ini - deteksi perubahan sedang berjalan, dari atas ke bawah (atau induk -> anak), dalam satu lintasan. Jadi, jika kita memiliki:
@Component({
selector: 'app-root',
template: `
<!-- initially falsey -->
<div *ngIf="sharedService.someValue">NgIf Block</div>
<child-comp></child-comp>
`
})
class AppRoot {
constructor(public sharedService:SharedService){}
}
@Component({
selector: 'child-component',
template: `child comp`
})
class ChildComponent {
constructor(public sharedService:SharedService){}
ngOnInit(){
this.sharedService.someValue = true;
}
}
Perubahan Detection akan dijalankan untuk aplikasi-akar pertama, periksa semua binding, dan kemudian berubah mendeteksi komponen anak - lagi, karena kita melakukan ini dalam single pass (bukan AngularJS-gaya keep checking sampai stabil), orang tua mengikat (* ngIf, dalam kasus ini) tidak diperiksa ulang - jadi sekarang aplikasi Anda dalam keadaan tidak konsisten (!)
Dalam mode pengembangan, kami benar-benar menjalankan deteksi perubahan itu dua kali - deteksi kedua kali dijalankan memverifikasi bahwa binding tidak berubah (yang akan menunjukkan status tidak konsisten), dan menampilkan error jika masalah seperti itu ditemukan. Anda dapat memverifikasi perilaku ini dengan memanggil enableProdMode()
di repro: https://plnkr.co/edit/GGEzdxK1eHzHyGiAdp56?p=preview. Perhatikan bahwa kesalahan tidak muncul (karena kita tidak menjalankan CD x2), tetapi aplikasi berakhir dalam keadaan tidak konsisten (anak ditampilkan, tetapi bukan * ngIf), yang merupakan Hal Buruk.
Alasan hal ini muncul baru-baru ini adalah bahwa sebelumnya, router menjalankan sejumlah pemeriksaan deteksi perubahan tambahan saat memasukkan komponen - ini tidak efisien dan menyebabkan masalah dengan animasi dan perutean, terutama ketika outlet router bersarang di ngIfs dan hal-hal semacam itu.
Saya berpendapat bahwa biasanya, kode yang melanggar aliran satu arah ini (seperti yang dilakukan repro di atas) tidak benar . Namun - ada kasus di mana Anda mungkin menginginkan perilaku ini (pada kenyataannya, kami memiliki penyiapan serupa di perpustakaan formulir sudut). Artinya, Anda perlu secara eksplisit memicu proses deteksi perubahan lain (yang akan kembali ke komponen root dan memeriksa dari atas ke bawah). Memanggil setTimeout
mencapai ini, meskipun biasanya lebih baik menggunakan Promise untuk memicu ini - lihat https://plnkr.co/edit/IoKMxEOlY9lY9LcWwFKv?p=preview
Semoga membantu!
@robwormald Kesalahan yang sama ini terjadi dengan skenario lain juga. Saya tidak yakin apakah ini mengatasi masalah ContentChildren . Mengapa Directive dapat mengubah nilai anak-anak selama AfterContentInit tanpa kesalahan tetapi melakukan hal yang sama dalam sebuah Komponen akan melempar kesalahan?
Juga, apakah diharapkan jika cdr.detectChanges()
dipanggil selama satu proses init, sehingga fungsi init lainnya tidak dipanggil? Ini adalah sesuatu yang ditemukan di utas ini sehingga saya membuat masalah terpisah .
@bayu_joo
Saya yakin @JSMike benar dalam penilaian bahwa kesalahan ini masih terjadi selama skenario di mana aliran satu arah berasal dari Parent-> Child menggunakan ContentChild (ren) dan ViewChild (ren).
Apakah dapat diterima untuk mempertimbangkan apa yang telah saya katakan sebelumnya , bahwa kesalahan ini lebih merupakan "peringatan" yang menyatakan bahwa pengembang melanggar "praktik terbaik" aliran satu arah tetapi tidak menyebabkan kesalahan kerusakan aplikasi, karena deteksi perubahan hanya berjalan sekali selama mode produksi (dunia nyata)?
@fugwenna Tidak, ini sebenarnya adalah kesalahan, karena jika tidak, tampilan dalam keadaan tidak konsisten. Sampel yang dijalankan Rob menceritakan semuanya.
Juga, ada kutipan ini dari dokumen
Aturan aliran data searah Angular melarang pembaruan tampilan setelah dibuat. Kedua kait ini menyala setelah tampilan komponen dibuat.
Jika Anda benar-benar perlu mengubah apa pun di sana, gunakan salah satu metode asinkron untuk melakukannya. setTimeout
mungkin cocok di sini, tetapi ketahuilah bahwa itu datang dengan (pada saat penulisan ini) penundaan 4ms sendiri. Cara lain adalah Promise.resolve().then(() => Assignment = here)
Yang lebih baik adalah mengambil langkah mundur, dan lihat mengapa Anda perlu melakukan perubahan di akhir proses ini dan lihat apakah ada cara yang lebih bersih untuk menyelesaikan masalah. Jika saya menemukan diri saya melakukan ini, saya memperlakukannya sebagai bau kode.
Terima kasih atas penjelasan detailnya @robwormald. Saya masih bingung mengapa seorang anak -> aliran data orang tua tampaknya masih berfungsi saat menggunakan pengikatan [hidden]
daripada *ngIf
(lihat https://plnkr.co/edit/L4QWK5pTgF1qZatFAN4u?p = pratinjau). Adakah ide mengapa ini masih berfungsi tanpa memberikan ExpressionChangedAfterItHasBeenCheckedError
?
Angular menggunakan fungsi checkAndUpdateView untuk melakukan deteksi perubahan.
Representasi singkat dari fungsi ini akan terlihat seperti:
function checkAndUpdateView(view) {
// update bound properties and run ngOnChanges/ngOnInit/ngDoCheck for child directives
updateDirectives
// check all embedded views that belong current view
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
// update DOM for the current view
updateRenderer
// perform checkAndUpdate for all child components
execComponentViewsAction(view, ViewAction.CheckAndUpdate);
}
Ini dijalankan secara rekursif berkat metode execEmbeddedViewsAction
dan execComponentViewsAction
. Dan juga titik kunci di sini adalah pemeriksaan sudut tampilan yang disematkan setelah arahan diperiksa dan sebelum memperbarui DOM.
Untuk menyederhanakan contoh @saverett saya akan menggunakan template berikut
<div *ngIf="!sharedService.displayList">Some text</div>
<router-outlet></router-outlet>
https://plnkr.co/edit/OdkzHjEXbEd3efYAisFM?p=preview
Kita sudah tahu bahwa *ngIf
hanyalah desugar. Begitu
<ng-template [ngIf]="sharedService.displayList">
<div>Some text</div>
</ng-template>
<router-outlet></router-outlet>
Kita dapat membayangkan struktur berikut:
my-app
|
NgIf directive
NgIf EmbeddedView
RouterOutlet directive
Apa yang dilakukan perintah RouterOutlet
?
Ini menyuntikkan komponen yang dirutekan ke ViewContainerRef
activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver|null) {
...
this.activated = this.location.createComponent(factory, this.location.length, injector);
Ini berarti bahwa router-outlet
membuat EmbeddedView
jadi setelah RouterOutlet
direktif menginisialisasi kita memiliki dua tampilan tertanam dalam tampilan AppComponent
.
my-app
|
NgIf directive ([ngIf]="sharedService.displayList")
NgIf EmbeddedView
RouterOutlet directive
BComponent EmbeddedView
Mari kita ilustrasikan dalam gambar
Sekarang mari kembali ke fungsi utama kita
function checkAndUpdateView(view) {
// update bound properties and run ngOnChanges/ngOnInit/ngDoCheck for child directives
updateDirectives
// check all embedded views that belong current view
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
...
// update DOM for the current view
updateRenderer
...
}
Angular akan melakukan pemeriksaan dengan urutan sebagai berikut:
NgIf directive ([ngIf]="sharedService.displayList") updateDirectives(1)
NgIf EmbeddedView execEmbeddedViewsAction(2)
RouterOutlet directive
BComponent EmbeddedView execEmbeddedViewsAction(3)
Direktif NgIf
akan diperiksa terlebih dahulu (di sini sharedService.displayList=false
diingat) dan kemudian metode NgOnInit
di dalam BComponent
akan dipanggil (di mana kami mengubah sharedService.displayList
menjadi true
) Dan ketika angular melakukan checkNoChanges
itu akan memunculkan kesalahan Expression has changed after it was checked
Kemudian mari kita ganti *ngIf
dengan [hidden]
. Sekarang kita hanya akan memiliki satu tampilan tertanam yang disisipkan oleh RouterOutlet
dan [hidden]="sharedService.displayList"
akan diperiksa dalam updateRenderer
my-app
|
div{Some Text} just a node
RouterOutlet directive
BComponent EmbeddedView
Angular akan melakukan pemeriksaan seperti ini:
function checkAndUpdateView(view) {
// check all embedded views that belong current view
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate); BComponent.ngOnInit (1)
...
// update DOM for the current view
updateRenderer update hidden binding (2)
...
}
Dengan cara ini pengikatan properti dom tidak akan menyebabkan kesalahan karena kami telah memperbarui setelah memanggil nilai ngOnInit
.
Itulah perbedaan utama antara arahan struktural *ngIf
dan properti dom mengikat [hidden]
Terima kasih untuk ikhtisar terperinci @alexzuza! Itu sangat membantu!
setTimeout () benar-benar berfungsi untuk saya :) Terima kasih @jingglang :)
.subscribe (data => setTimeout (() => this.requesting = data, 0))
@robwormald benar di sini. Saya memiliki masalah yang sama dan lebih sulit dideteksi karena saya menggunakan NGRX dan observable. Masalah yang saya hadapi adalah bahwa ada wadah komponen di berbagai tingkat dalam hierarki rute yang berlangganan status toko. Segera setelah saya memindahkan komponen penampung saya ke tingkat yang sama dalam modul fitur saya, masalahnya hilang. Saya tahu ini adalah jawaban yang berbelit-belit tetapi jika Anda kebetulan menggunakan pola wadah / komponen NGRX semoga ini mengarahkan penyelidikan Anda ke arah yang benar
Dengan setTimeOut berfungsi, terima kasih @jingglang
ngAfterViewInit () {
this.innerHeight = (window.innerHeight);
setTimeout (() => this.printPosition (), 0);
}
@alexzuza , ini tidak terjadi hanya untuk ngIf, ini terjadi (setidaknya untuk saya) juga dengan [ngClass]
@ Ninja-Coding-Git Saya baru saja menjelaskan kasus tertentu. Saya tahu itu bisa terjadi dengan kode apa pun.
Bagi saya, memanggil ChangeDetectorRef setelah mengubah elemen menyelesaikan masalah:
import { Component, ChangeDetectorRef } from '@angular/core'
@Component({
selector: 'my-app',
template: `<div>I'm {{message}} </div>`,
})
export class App {
message: string = 'loading :(';
constructor(private _cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
this.message = 'all done loading :)'
this._cdr.detectChanges();
}
}
Solusi ditemukan di sini
halo @thetylerb! bisakah Anda menjelaskan lebih lanjut tentang ini. Kami menggunakan ngrx dan saya tidak tahu bagaimana mengkompensasinya. Saya pikir menggunakan promise di atas observable cukup canggung.
@ piq9117 Anda juga dapat melihat komentar ini https://github.com/angular/angular/issues/18129#issuecomment -326801192 Saya membuat interaksi antara deteksi perubahan dan ngrx, ini dapat membantu saya kira
@victornoel terima kasih! Itu cukup menggambarkan situasi kita. Kami mendapatkan struktur data yang tidak dapat diubah (immutablejs), dan kami menggunakan pipa asinkron di mana-mana. Pikiran awal saya adalah, toko akan mengubah pohon komponen ketika toko telah terhidrasi, dan pohon komponen hanya akan mengkompensasi perubahan ini dan melakukan perubahan lagi
Kami mendapatkan struktur data yang tidak dapat diubah (immutablejs), dan kami menggunakan pipa asinkron di mana-mana. Pikiran awal saya adalah, toko akan mengubah pohon komponen ketika toko telah terhidrasi, dan pohon komponen hanya akan mengkompensasi perubahan ini dan melakukan changeDetection lagi.
Dalam proyek saya, kami memiliki beberapa jenis struktur data yang dinormalisasi (bukan struktur data bersarang), jadi itu mengurangi sedikit masalah semacam ini, sampai tidak! :HAI
Anda hanya berharap:
Saya menggunakan ngrx dan mendapat situasi seperti ini:
Dalam app.component.html:
<div *ngIf='(appState | async).show'>test</div>
Dan di app.component.ts
this.appState= this.store.select("app");
Tentu saja dalam situasi ini saya mendapatkan error, tetapi ketika saya menambahkan flag boolean di komponen seperti ini:
app.component.html -> <div *ngIf='show'>test</div>
app.component.ts ->
this.appState.subscribe((state: AppState) => {
this.show= state.show;
})
Errornya hilang, tentu bisa dimaklumi buat saya kenapa, tapi saya ingin tahu pendapat anda, apakah ini solusi yang baik? Apa pro dan kontra ??
Tidak menggunakan ngrx tetapi saya mengalami masalah dengan pemeriksaan sederhana dengan querySelectorAll let chips = [].map.call(document.querySelectorAll('li.chip'), this.chipVisible);
Solusi @bogdancar dengan ChangeDetectorRef.detectChanges()
menekan kesalahan tetapi alangkah baiknya jika tidak harus mengimpor ChangeDetectorRef dengan memperbaiki apa pun yang menyebabkan kesalahan.
Saya mendapat masalah yang sama ketika saya menggunakan rxjs di sudut 4.4.4. Saya hanya berlangganan objek observasi yang disediakan oleh layanan DI. Pekerjaan saya di bawah ini:
this.eventBroadcastService.getCustomEvent({type: 'loginOpen'}).subscribe(e => {
setTimeout(() => {
this.isBlur = true;
}, 0);
});
Saya menggunakan setTimeout untuk bekerja. Sekarang berhasil, tetapi rumit. Saya merasa tidak enak.
ngOnChanges (perubahan: SimpleChanges | any) melakukan trik
Masalah telah diperbaiki hanya setelah memindahkan logika parameter perubahan dinamis di dalam metode ngDoCheck ().
ngDoCheck(): void {
this.checkValid = this.sourceArr.includes((this.chanedVal).toUpperCase());
}
@ asrinivas61 Saya rasa ini adalah ide yang sangat buruk. ngDoCheck
sering dipanggil oleh Angular dan jika sourceArr
memiliki banyak data, hal itu dapat mengganggu kinerja Anda.
Lihat https://angular.io/guide/lifecycle-hooks#other -angular-lifecycle-hooks
Saya lebih suka merekomendasikan untuk saat ini menggunakan ngAfterViewInit
dengan setTimeout
:
setTimeout(_ => /* your code here */, 1000);
contoh:
public ngAfterViewInit(): void {
// We use setTimeout to avoid the `ExpressionChangedAfterItHasBeenCheckedError`
// See: https://github.com/angular/angular/issues/6005
setTimeout(_ => this.isLoadingInProgress = false, 1000);
}
@dgroh Anda tidak perlu menambahkan waktu tunggu aktual, Anda hanya ingin js menjalankan kode Anda dalam tugas asinkron berikutnya yang berarti deteksi perubahan lain dijalankan.
@victornoel Saya barelly percaya ini akan bekerja dengan observable kecuali Anda secara eksplisit memanggil detectChanges
dari ChangeDetectorRef
. Saya berharap semua ini tidak diperlukan.
@dgroh Anda pasti salah paham, saya hanya menganjurkan untuk menulis:
public ngAfterViewInit(): void {
// We use setTimeout to avoid the `ExpressionChangedAfterItHasBeenCheckedError`
// See: https://github.com/angular/angular/issues/6005
setTimeout(_ => this.isLoadingInProgress = false);
}
Apakah tidak mungkin menambahkan langkah setelah ngAfterViewInit
seperti setTimeout
? (misalnya: ngAfterViewLoad
)
@ aks4it , ini tampaknya berhasil, tapi saya ingin detail lebih lanjut tentang cara kerjanya? Apakah kode pembuatan komponen dinamis sinkron?
Demo plunker resmi dari dokumen sudut memiliki kesalahan yang sama. Bisakah Anda memperbaikinya di sana? :) Terima kasih
@tokopedia
ngAfterViewInit() {
//this.loadComponent();
this.getAds();
}
ini bekerja?
saya tidak mengerti kebutuhan Anda tetapi Anda benar-benar ingin interval 3 detik? jika ya ini berhasil, dan bahkan jika Anda ingin langsung memanggil loadComponent saya berhasil dengan:
ngAfterViewInit() {
setTimeout(()=>{this.loadComponent()},0)
}
@istiti Saya dapat memperbaikinya dengan ChangeDetectorRef
solusi, menempatkan detectChanges()
setelah ... data = data
Tetapi karena ini berasal dari dokumen resmi, saya ingin tahu tentang solusi resmi dari masalah tersebut. Bukan timeout atau ChangeDetectorRef.
Maaf saya terlalu singkat di posting pertama .. Saya ingin menunjukkan tingkat keparahan masalah, karena bahkan dokumen sudut memiliki kode yang sama.
FYI, saya tidak yakin apa yang sebenarnya menyebabkan di sini, Tapi kesalahan hilang ketika saya mengaktifkan mode prod di main.ts
`enableProdMode ()
// if (environment.production) {
// enableProdMode ();
//} `
"@ angular / core": "^ 4.2.4"
"skrip ketikan": "~ 2.3.3"
@bakthaa ... itu seharusnya perilaku karena pemeriksaan kontrol (apakah komponen @Inputs()
nilai adalah sama) yang dipanggil setelah setiap siklus CD dipanggil hanya dalam mode pengembangan dan bukan dalam mode produksi aplikasi Angular. Jadi masalahnya tersembunyi dalam mode produksi tetapi masih ada ... dan mungkin dapat menjadi sumber efek samping yang tidak diinginkan atau menyesatkan yang berbeda dalam mode produksi.
Saya menggunakan Angular 5, saya dapat memperbaiki masalah ini dengan menginjeksi komponen ChangeDetectorRef dan memaksanya untuk mendeteksi perubahan.
constructor(private changeDetector: ChangeDetectorRef) {}
ngAfterViewChecked(){
this.changeDetector.detectChanges();
}
@ Sabri-Bouchlema ... ya, ini satu cara ... tapi tetap saja Anda harus berpikir ulang sekali lagi ... mengapa komponen @Inputs
diubah selama satu siklus CD ... dan putuskan apakah Logikanya benar ... dan memang benar bahwa dari 95% kasus, kode dapat diubah untuk menghindari masalah tanpa memanggil tambahan detectChanges()
.
Menggunakan sudut 5.1.2. Saya mendapatkan kesalahan ini saat membuka dialog material di ngAfterViewChecked. Seperti yang dilaporkan @ aks4it , saya harus membungkus kode ke dalam setTimeout untuk membuatnya berfungsi.
Saya menghadapi masalah yang sama dengan versi 4.01
ng-version = "4.0.1
Sama di sini dengan sudut 4.4.6
Perbaiki menggunakan this.changeDetector.detectChanges();
Saya telah menulis plunker ini menggunakan variabel template dan saya tidak mengerti mengapa saya memiliki ExpressionChangedAfterItHasBeenCheckedError. Jika saya mengubah urutan komponen saya, kesalahan menghilang:
https://plnkr.co/edit/Mc0UkTR2loI7wgmLAWtX
Saya telah mencoba changeDetector.detectChanges tetapi tidak berhasil
@AlexCenteno ... karena logika evaluasinya adalah dari atas ke bawah ... dan value
properti comp1
disetel @Input() public value:String=null;
terlebih dahulu dan kemudian disetel ulang menggunakan referensi comp2.value
... <div comp1 [value]="comp2.value"></div>
yang diset sementara ke Hello
melalui @Input()
... semua dalam satu siklus CD ... jadi pemeriksaan berulang jika input pada komponen anak sama seperti pada awal siklus CD dalam mode dev mendeteksi perubahan pada properti value
pada comp1
.
hai @ mlc-mlapis terima kasih atas waktunya. Jadi tidak ada cara untuk menghindari kesalahan? Saya rasa itu adalah sesuatu yang tidak perlu saya khawatirkan setelah saya memanggil enableProdMode?
@AlexCenteno ... ya, dalam mode prod, pemeriksaan setelah siklus CD tidak dipanggil ... tetapi itu tidak berarti Anda harus puas dengan itu. Anda harus menerapkan logika yang berbeda dengan meneruskan komponen anak silang value
. Ada juga masalah bahwa <div>
tidak memiliki properti value
. Mengapa Anda menggunakan komponen atribut sama sekali? Mengapa tidak hanya:
<comp1 [value]="comp2.value"></comp1>
<comp2 #comp2 value="Hello"></comp2>
karena menggunakan @Output()
pada <comp2>
akan baik-baik saja dan memungkinkan Anda untuk memperbarui [value]
pada comp1
tanpa kesalahan.
Menggunakan [email protected]
dengan angular@5
standart store.select
peringatan ExpressionChangedAfterItHasBeenCheckedError
dalam mode pengembang dan salah bekerja pada mode prod. Pengiriman dan pemilihan berada pada level / wadah yang berbeda.
@Component({
...
template `
...
<div [ngClass]="{
'home__sidenav': true,
'home__sidenav--active': (isActiveSidenavMenu$ | async)
}">
...
`
})
export class HomeContainer {
isActiveSidenavMenu$ = this.store.select(fromHome.isActiveSidenavMenu);
...
}
Pendekatan ini menghapus pesan peringatan, tetapi tampaknya lebih rumit:
@Component({
...
template `
...
<div [ngClass]="{
'home__sidenav': true,
'home__sidenav--active': isActiveSidenavMenu
}">
...
`
})
export class HomeContainer {
isActiveSidenavMenu$ = this.store.select(fromHome.isActiveSidenavMenu);
isActiveSidenavMenu;
ngOnInit() {
this.isActiveSidenavMenu$.subscribe(res => {
this.isActiveSidenavMenu = res;
this.cd.detectChanges();
});
}
}
Apakah ada ide bagaimana mengatasi masalah ini dengan lebih benar?
@rimlin sudahkah Anda mencoba langsung berlangganan ke toko?
ngOnInit() {
this.store.select(fromHome.isActiveSidenavMenu)
.subsbrice(res => {
this.isActiveSidenavMenu = res;
});
}
saya pikir saya punya masalah serupa. dan dengan menghindari async
dalam template memecahkan masalah.
@ piq9117 terima kasih atas sarannya, mencoba solusi Anda, tampaknya lebih jelas dengan lebih sedikit kode, tetapi di sini juga perlu memanggil this.cd.detectChanges();
, jika tidak tampilan tidak akan diperbarui.
ngOnInit() {
this.store.select(fromHome.isActiveSidenavMenu).subscribe(res => {
this.isActiveSidenavMenu = res;
this.cd.detectChanges();
});
}
Saya menggunakan Angular 5.2, saya melakukan sesuatu seperti itu. Bekerja untuk saya
constructor(private changeDetector: ChangeDetectorRef) {}
ngAfterViewInit(){
this.changeDetector.detectChanges();
}
Menggunakan Angular 5.2, diperbaiki menggunakan detectChanges ()
Ya, kami juga memperbaikinya menggunakan rute detectChanges () (Angular 5.1). Saya bersama banyak orang lain di sini meskipun yang berpikir ini masih hanya solusi dan tidak boleh diperlakukan sebagai jawaban dalam jangka panjang. Harus ada pengait siklus hidup lain yang bisa digunakan.
Memicu siklus deteksi perubahan yang sama sekali baru untuk memungkinkan perubahan seperti ini sepertinya kurang optimal.
Hal ini sepertinya terus berlangsung meskipun semua jawaban dan saran sudah diposting di atas.
ngAfterViewInit
dan menjadi ngOnInit
dalam beberapa kasus.this.changeDetectorRef.detectChanges();
adalah cara resmi dan dikenal untuk menangani ini dalam kurang dari 5% kasus di mana langkah pertama bermasalah.Catatan lain:
ngAfterViewInit
karena mengubah nilai yang digunakan di DOM dapat menyebabkan kesalahan ini.Check
atau Checked
karena mereka mungkin dipanggil pada setiap siklus deteksi perubahan.setTimeout
dapat mengatasi ini, tetapi dua solusi di atas lebih disukai.Terakhir, ingatlah bahwa kesalahan ini tidak menunjukkan masalah dengan Angular. Kesalahan ini di sini untuk memberi tahu Anda bahwa Anda telah melakukan kesalahan dalam kode Anda dan bahwa Anda melanggar sistem aliran data satu arah yang membuat Angular cepat dan efisien.
Postingan di atas dan banyak postingan blog membahas hal ini lebih dalam dan layak dibaca karena Anda harus memahami model aliran data satu arah ini dan cara membuat komponen serta meneruskan data di dalamnya.
@Splaktar Apakah lebih baik mengunci masalah ini (dan menghapus saya) untuk membuat komentar di atas menonjol? Utasnya sudah terlalu panjang dan kebanyakan hanya berulang. 😃
@Splaktar Anda membuat poin ini "Ini berarti bahwa tidak ada komponen anak yang dapat mengubah nilai yang digunakan dalam template komponen induk".
Misalkan Anda memiliki komponen penampung yang mencakup navigasi, judul halaman, dll., Dan memuat kumpulan sub-komponen, masing-masing dapat memuat sub-komponennya sendiri. Komponen yang dimuat ke dalam penampung akan berubah berdasarkan data, dan sub-komponen yang dimuatnya akan berubah berdasarkan data. Masing-masing sub-komponen ini harus dapat berkomunikasi kembali ke informasi penampung induk tentang apa itu sehingga penampung induk dapat menampilkan Nav tingkat atas yang sesuai kepada pengguna (mungkin jejak runut tautan, atau yang serupa), juga sebagai informasi yang dapat digunakan dalam judul untuk mengidentifikasi di tingkat penampung apa yang dimuat ke dalam tampilan.
Dalam hal ini, yang merupakan arsitektur yang sangat umum dalam pengalaman saya, saya akan menggunakan layanan negara umum yang sub-komponen akan mendaftar sendiri, dan komponen induk berlangganan sehingga anak-anak dapat mendidik orang tua, dan orang tua. dapat menampilkan informasi yang sesuai.
Ini tidak mengikuti pendekatan bottom-up, karena induk akan mendapatkan semua data yang relevan dari layanan, dan mendorong informasi apa pun yang diperlukan oleh komponen turunan melalui komunikasi satu arah.
Ini tampaknya bertentangan dengan apa yang Anda sarankan di atas, dan bagi saya adalah sumber masalahnya. Ketika komponen anak mendaftar dengan layanan, komponen induk melontarkan kesalahan.
Apakah ini salah?
Dan jangan pernah lupa, bahwa ada bug yang disetujui:
https://github.com/angular/angular/issues/15634
@ Martin-Wegner Silakan lihat https://github.com/angular/angular/pull/18352#issuecomment -354170352
@alignsoft ... apakah Anda memiliki reproduksi sederhana dari kasus Anda dan masalahnya sebagai Stackblitz? Karena tampaknya ada hal lain yang memengaruhi kasus daripada sekadar logika yang Anda jelaskan.
@trotyl solusi dari @Splaktar tidak terkait dengan masalah sederhana saya di sini https://github.com/angular/angular/issues/17572#issuecomment -311589290 atau?
@ Martin-Wegner ... dalam kasus Anda, cukup mengubah ngAfterViewInit
menjadi ngOnInit
hook.
@alignsoft , seperti yang dikatakan @ mlc-mlapis, contoh Stackblitz akan sangat membantu dalam mendemonstrasikan arsitektur yang tepat untuk kasus ini. Ini sangat mungkin dilakukan dengan menggunakan set layanan yang tepat, observable, hook siklus hidup, dll. Kasus khusus ini juga akan menjadi topik yang baik untuk StackOverflow.
@Splaktar Setelah saya mendapatkan tenggat waktu saya saat ini dari meja saya, saya akan membuat mockup sebuah proyek yang menunjukkan kasus sederhana.
@ mlc-mlapis wow terima kasih, berhasil :)
@alignsoft , hari ini saya membuat aplikasi yang berperilaku persis seperti yang Anda jelaskan. @ mlc-mlapis, Anda dapat menemukan contoh yang dipreteli di ExpressionChangedAfterItHasBeenCheckedError
akan ditampilkan.
Dalam contoh ini, admin.component bertindak sebagai komponen induk yang bertanggung jawab atas 3 hal:
<router-outlet></router-outlet>
.<div class="ticker-container" *ngIf="tickedMessage !== ''">
<h1>{{tickedMessage}}</h1>
<button type="button" (click)="tickedMessage = ''">
close
</button>
</div>
<div class="side-nav">
<h2>side nav</h2>
<ul>
<li><a routerLink="/">dashboard</a></li>
<li><a routerLink="/apps/thirdpartycms">CMS System</a></li>
</ul>
</div>
<div class="main-content-container">
<router-outlet></router-outlet>
</div>
export class AdminComponent implements OnInit, OnDestroy {
private _tickedMessageSubscription: Subscription;
tickedMessage: string = '';
constructor(
private router: Router,
private adminService: AdminService
) { }
ngOnInit() {
/*
On the side nav if you click on CMS System, this piece of code throws the exception:
"ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed
after it was checked. Previous value: 'false'. Current value: 'true'.""
*/
this._tickedMessageSubscription = this.adminService.tickedMessageAnnounced$.subscribe((tickedMessage: string) => {
this.tickedMessage = tickedMessage;
});
}
ngOnDestroy() {
this._tickedMessageSubscription.unsubscribe();
}
}
Dalam meneliti cara mengkomunikasikan data kembali ke komponen induk ketika menggunakan <router-outlet></router-outlet>
, saya membaca bahwa jika saya menggunakan @Output
untuk mengirim data kembali ke komponen induk, petunjuk router-outlet akan berakhir menangani acara tersebut. Karena ini adalah acara khusus, petunjuk outlet-router tidak akan tahu cara menanganinya. Jadi saya melewatkan metode ini. Setelah saya membaca tentang layanan bersama di angular.io, saya menulis sendiri:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class AdminService {
private _tickedMessageAnnounced = new Subject<string>();
tickedMessageAnnounced$ = this._tickedMessageAnnounced.asObservable();
announceTickedMessage(tickedMessage: string) {
this._tickedMessageAnnounced.next(tickedMessage);
}
}
Setelah salah satu komponen anak memanggil this.adminService.announceTickedMessage(this._tickedMessage);
:
import { Observable } from 'rxjs/Observable';
import { Component, ViewChild, OnInit, Inject } from '@angular/core';
import { AdminService } from '../../../core/admin/admin.service';
@Component({
selector: 'cms',
templateUrl: './thirdparty-cms.component.html',
styleUrls: ['./thirdparty-cms.component.scss'],
})
export class ThirdPartyCmsComponent implements OnInit {
private _tickedMessage: string;
constructor(private adminService: AdminService) { }
ngOnInit(): void {
this._tickedMessage = 'Please do not create an entry for a page URL that does not exist in the CMS system';
this.adminService.announceTickedMessage(this._tickedMessage);
}
}
komponen induk melontarkan pengecualian ExpressionChangedAfterItHasBeenCheckedError
karena mencoba memperbarui salah satu properti publiknya dengan nilai pesan yang dicentang yang dikirim oleh komponen anak:
export class AdminComponent implements OnInit, OnDestroy {
private _tickedMessageSubscription: Subscription;
tickedMessage: string = '';
constructor(
private router: Router,
private adminService: AdminService
) { }
ngOnInit() {
/*
On the side nav if you click on CMS System, this piece of code throws the exception:
"ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed
after it was checked. Previous value: 'false'. Current value: 'true'.""
*/
this._tickedMessageSubscription = this.adminService.tickedMessageAnnounced$.subscribe((tickedMessage: string) => {
this.tickedMessage = tickedMessage;
});
}
ngOnDestroy() {
this._tickedMessageSubscription.unsubscribe();
}
}
Apa yang memperbaiki ExpressionChangedAfterItHasBeenCheckedError
adalah dengan menjalankan panggilan berlangganan kembali secara asinkron dan menunggu pesan yang dicentang:
export class AdminComponent implements OnInit, OnDestroy {
private _tickedMessageSubscription: Subscription;
tickedMessage: string = '';
constructor(
private router: Router,
private adminService: AdminService
) { }
ngOnInit() {
/*
This clears the ExpressionChangedAfterItHasBeenCheckedError exception.
*/
this._tickedMessageSubscription = this.adminService.tickedMessageAnnounced$.subscribe(async (tickedMessage: string) => {
this.tickedMessage = await tickedMessage;
});
}
ngOnDestroy() {
this._tickedMessageSubscription.unsubscribe();
}
}
Berasal dari dunia WPF, sepertinya kita perlu memberi tahu utas UI untuk menunggu panggilan balik langganan yang dapat diamati karena yang dapat diamati dieksekusi secara asinkron?
Saya baru saja memulai dengan Angular jadi mohon perhatiannya terhadap skill Angular saya: P
@ktabarez Terima kasih, ini banyak membantu saya!
@ktabarez Terima kasih! Sepertinya itu bekerja dengan baik! Saya membungkus langganan di komponen induk dalam waktu tunggu, yang juga berfungsi, tetapi merasa kotor.
Saya melihat bahwa ketika menginterogasi status redux itu dijamin akan dikirimkan tetapi pada startup seperti itu ketika dimasukkan ke dalam ngOnInit sebagai lawan dari konstruktor
@ktabarez Terima kasih banyak!
Solusi async ... await in subscribe
berfungsi.
@ktabarez Solusi Anda pada dasarnya melakukan hal yang sama seperti setTimeout ({}, 0) tetapi terasa lebih elegan. Sangat menyukainya! Itu juga membuat saya menyadari bahwa apa pun bisa ditunggu (hampir seperti C # menunggu Task.FromResult (tickedMessage)), jadi bersoraklah untuk itu!
Jadi, pertanyaan: mengapa kita harus melakukan async / await pada perhitungan properti pada observable yang mendapatkan data dari back end yang mungkin sering berubah? Bagaimana dengan ngClass membuat ini perlu? Saya mendapatkan ini saat menggunakan {observable | pipa async}. Jadi async / await bukanlah apprach terbaik untuk saya. (atau pipa asinkron bukanlah pendekatan terbaik)
Saya mengalami masalah ini di mana komponen induk saya memiliki beberapa komponen anak. Salah satu komponen anak memiliki Input yang membutuhkan data dari properti komponen induk dan properti mendapatkan datanya dari Observable / Promise (mencoba keduanya).
parent.component.html
<mat-tab-group>
<mat-tab>
<app-child-1></app-child-1>
</mat-tab>
<mat-tab>
<app-child-2></app-child-2>
</mat-tab>
<mat-tab>
<app-child-3 [datasource]="data"></app-child-3>
</mat-tab>
</mat-tab-group>
parent.component.ts
// ... omitted...
data: any;
ngOnInit() {
this.service1.getMethod(some_id).then(data => this.data = data);
}
Saya menemukan setiap solusi yang ditemukan di utas. Tapi tidak ada yang berhasil dalam kasus saya. Untungnya karena beberapa orang mengisyaratkan bahwa ini adalah masalah karena memanipulasi DOM, seperti meletakkan ekspresi ke _ (ngIf) _ sebagai contoh.
Jadi untuk menghindari hal ini saya mencoba bereksperimen dengan ng-template , ng-container dan ngTemplateOutlet .
<ng-container *ngTemplateOutlet="data ? content : loading"></ng-container>
<ng-template #content>
<mat-tab-group>
<mat-tab>
<app-child-1></app-child-1>
</mat-tab>
<mat-tab>
<app-child-2></app-child-2>
</mat-tab>
<mat-tab>
<app-child-3 [datasource]="data"></app-child-3>
</mat-tab>
</mat-tab-group>
</ng-template>
<ng-template #loading>Loading your component. Please wait</ng-template>
Saya dapat menyelesaikan ini tanpa menggunakan ChangeDetectorRef
atau setTimeout()
Bagi saya, kesalahan ini terjadi saat menampilkan / menyembunyikan bidang sesuai dengan kondisi, saya dapat menyelesaikannya setelah menerapkan nilai-nilai pengaturan bidang:
this.form.get ('field').updateValueAndValidity();
Saya telah mencoba melakukan ini dengan mat-dialog yang terbuka di init, dan saya benar-benar telah mencoba setiap solusi yang pernah saya lihat di utas ini (setel batas waktu, penyelesaian janji, pipa yang dapat diamati, setelah tampilan init, setelah konten init) dan kombinasi di dalamnya. Masih tidak berhasil. Kemudian saya melihat posting @Splaktar tentang bagaimana ini melanggar aliran data satu arah, jadi saya benar-benar membuat bendera di ngrx yang mengatakan untuk membuka dialog dan berlangganan itu, yang awalnya salah dan saya masih mendapatkan kesalahan ini. Apa yang harus saya lakukan? Apakah benar-benar tidak ada cara untuk membuka dialog modal tanpa melampirkannya ke acara yang benar-benar terpisah?
Di init:
this.store.pipe(
select(getShouldLogin),
).subscribe((shouldLogin: boolean) => {
if (shouldLogin) {
this.openLoginDialog();
}
});
this.checkUserId();
Fungsi tambahan:
checkUserId() {
const id = this.cookies.getUserId();
if (!id || id === 'undefined') {
this.connApi.setShouldLogin(true);
}
}
openLoginDialog() {
const dialogRef = this.dialog.open(LoginDialogComponent, {
width: '400px',
height: '300px',
});
dialogRef.afterClosed().subscribe(result => this.handleLogin(result));
}
Menggunakan angular 6.0.0-rc.1
Saya menggunakan angular dengan ngrx dan mendapatkan kesalahan ini setelah mengirimkan formulir.
diperbaiki dengan pengaturan
ChangeDetectionStrategy.OnPush
pada komponen formulir
masih mengalami masalah. mencoba segala macam solusi. setTimeout
berfungsi pertama kali, tetapi pemicuan berikutnya dari dialog.open
menghasilkan kesalahan yang sama. Ada status tentang ini?
Saya menghadapi masalah yang sama. Mencoba segalanya tetapi tampaknya masalah ini muncul pada aliran data satu arah.
Di sini saya mencoba memperbarui yang dipilih. di mana masalah terpecahkan tetapi kesalahan yang sama muncul.
<p-row> <p-column class="text-left" footer="{{selectedWoids.length}} {{getWoidString(selectedWoids.length)}} selected out of {{getTotalCounts()}} {{getWoidString(getTotalCounts())}}" colspan="11"></p-column> </p-row>
private setSelectedWoidsCount () {
if (this.isSelectAllWoids) {
this.selectedWoids = this.allWoids;
}
}
Karena Anda memutus pipeline rendering Angular ...
Saya melihat kesalahan serupa "ExpressionChanged ..." setelah beralih dari
4.1.2
ke4.2.3
.Dalam kasus saya, saya memiliki dua komponen yang berinteraksi melalui layanan. Layanan ini disediakan melalui modul utama saya, dan
ComponentA
dibuat sebelumComponentB
.Di
4.1.2
, sintaks berikut bekerja dengan baik tanpa kesalahan:_ComponentA Template: _
<ul *ngIf="myService.showList"> ...
_ComponentB Kelas: _
ngOnInit() { this.myService.showList = false; //starts as true in the service ... }
Di
4.2.3
, saya harus memodifikasi templateComponentA
sebagai berikut untuk menghindari kesalahan "ExpressionChanged ...":_ComponentA Template: _
<ul [hidden]="!myService.showList"> ...
Saya masih mencoba mencari tahu mengapa ini baru saja menjadi masalah, dan mengapa peralihan dari
*ngIf
menjadi[hidden]
berfungsi.
Apakah Anda mengetahui cara kerjanya saat beralih dari ngIf ke [tersembunyi]
Apakah ada solusi untuk masalah ini di Angular 6 ??
@alokkarma ... terlalu tua ... Anda harus memindahkannya ke Angular 6 atau setidaknya 5. Tetapi kode contoh Anda terlihat jelas ... Anda mengubah properti layanan di komponen anak B, dan ini memengaruhi beberapa perilaku di komponen anak A. Kedua komponen anak ditempatkan di induk yang sama, sehingga ketiganya diproses secara bersamaan di hubungan dalam satu siklus CD. Apa yang kamu harapkan?
Fakta bahwa kasus yang sama berfungsi di versi 4.1.2 terkait dengan beberapa bug yang dihilangkan di versi yang lebih baru, termasuk 4.2.3.
Saya menggunakan Angular 6. Saya menggunakan MessageService untuk berkomunikasi antara komponen otentikasi saya dan komponen aplikasi saya untuk menampilkan konten bilah navigasi yang berbeda jika pengguna masuk (atau tidak)
Ini berfungsi dengan baik terlepas dari kesalahan ini!
Saya sudah mencoba solusi lain seperti EventEmitter tetapi tidak berhasil! atau saya mendapatkan kesalahan yang sama.
@dotNetAthlete ... tunjukkan demo reproduksi sederhana di Stackblitz. Sulit membicarakannya tanpa kode yang sebenarnya.
@dotNetAthlete Saya menggunakan mekanisme dasar yang sama di mana saya memiliki layanan negara yang berisi judul halaman sebagai subjek perilaku (asObservable) di komponen penampung utama, dan komponen anak dimuat ke dalamnya yang menetapkan judul halaman yang sesuai di layanan negara untuk diamati dan ditampilkan oleh wadah induk.
Selalu ada kesalahan 'Ekspresi berubah' ketika penampung menginisialisasi nilai dan kemudian komponen anak segera memperbaruinya, jadi saya melakukan ini sekarang di induk / penampung:
this.stateSvc.currentPageTitle$
.subscribe(
(async (pageTitle) => {
this.pageTitle = await pageTitle;
}))
Dimana layanan negara memiliki ini:
// Store
private currentPageTitleStore = new BehaviorSubject<string>("");
// Getter (as Observable)
public currentPageTitle$ = this.currentPageTitleStore.asObservable();
// Setter
public setCurrentPageTitle(pageTitle: string) {
this.currentPageTitleStore.next(pageTitle);
}
Ini menghilangkan masalah. Solusi ini didasarkan pada beberapa umpan balik bermanfaat dari orang lain di utas ini.
@alignsoft - solusi cantik - masalah diperbaiki di aplikasi Angular 6.
Saya melihat ini di halaman dimuat ulang di angular 7 sekarang ...
Saya menetapkan nilai komponen anak pada orang tua ngAfterViewInit
Harus melakukan ini pada komponen anak
public activate() {
setTimeout(() => this.active = true);
}
Saya menemukan solusinya dalam utas masalah ini https://github.com/angular/angular/issues/10762
Setelah saya menggunakan hook siklus hidup AfterContentInit
, kesalahan akan hilang.
@alignsoft , hanya menambahkan perubahan Anda, karena kami menggunakan BehavioralSubject alih-alih Subjek,
Saya menyempurnakannya sebagai berikut:
\\ Component
export class AppComponent implements OnInit {
isLoading: Boolean;
constructor(private loaderService: LoaderService) { }
ngOnInit(){
this.loaderService.isLoading.subscribe(async data => {
this.isLoading = await data;
});
}
}
\\ Service
@Injectable({
providedIn: 'root'
})
export class LoaderService {
// A BehaviorSubject is an Observable with a default value
public isLoading: BehaviorSubject<Boolean> = new BehaviorSubject(false);
constructor() {}
}
\\ Any other component or in my case, an interceptor that has the LoaderService injected
this.loaderService.isLoading.next(true);
Tetapi saya setuju dengan @daddyschmack , saya ingin mendapatkan sumber daya apa pun yang menunjukkan info mengenai withotu salah satu perbaikan yang disebutkan di atas ( cdr.detectionChanges()
atau async await
atau aftercontentchecked
), mengapa [hidden]
atribut berfungsi dan bukan *ngIf
@acidghost Terima kasih atas tautan itu, ini memecahkan masalah saya. Saya sebenarnya sudah menyerah untuk mencoba memperbaikinya di satu area aplikasi saya tetapi ketika itu mulai terjadi lagi di area baru ...
Masalah ini telah dikunci secara otomatis karena tidak ada aktivitas.
Ajukan masalah baru jika Anda mengalami masalah serupa atau terkait.
Baca lebih lanjut tentang kebijakan penguncian percakapan otomatis kami.
_Tindakan ini telah dilakukan secara otomatis oleh bot._
Komentar yang paling membantu
Saya mendapatkan masalah yang sama setelah memutakhirkan dari 4.1.3 ke 4.2.3. Menggunakan setTimeout memperbaiki masalah.
dari:
PanictUtil.getRequestObservable (). Subscribe (data => this.requesting = data);
untuk:
PanictUtil.getRequestObservable (). Subscribe (data => setTimeout (() => this.requesting = data, 0));