ExpressionChangedAfterItHeenBeenCheckedError อธิบายแล้ว


308

โปรดอธิบายกับฉันว่าทำไมฉันถึงได้รับข้อผิดพลาดนี้: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.

เห็นได้ชัดว่าฉันได้รับมันในโหมด dev เท่านั้นมันไม่ได้เกิดขึ้นในงานสร้างของฉัน แต่มันก็น่ารำคาญมากและฉันก็ไม่เข้าใจถึงประโยชน์ของการมีข้อผิดพลาดในสภาพแวดล้อม dev ของฉันที่จะไม่ปรากฏขึ้นบนผลิตภัณฑ์ - อาจเป็นเพราะฉันขาดความเข้าใจ

โดยปกติการแก้ไขนั้นง่ายพอฉันเพิ่งตัดข้อผิดพลาดที่ทำให้เกิดรหัสใน setTimeout ดังนี้:

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

หรือบังคับให้ตรวจจับการเปลี่ยนแปลงด้วยตัวสร้างเช่นนี้constructor(private cd: ChangeDetectorRef) {}:

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

แต่ทำไมฉันจึงพบข้อผิดพลาดนี้อยู่ตลอดเวลา ฉันต้องการที่จะเข้าใจเพื่อที่จะได้หลีกเลี่ยงการแก้ไขแฮ็กเหล่านี้ในอนาคต


11
ทุกสิ่งที่คุณต้องรู้เกี่ยวกับข้อผิดพลาด ExpressionChangedAfterItHasBeenCheckedErrorอธิบายพฤติกรรมนี้ในรายละเอียดที่ดีเยี่ยม
Max Koretskyi

คำตอบ:


121

ฉันมีปัญหาที่คล้ายกัน เมื่อดูที่เอกสารประกอบการขอข้อมูลเกี่ยวกับวัฏจักรชีวิตฉันเปลี่ยนngAfterViewInitไปngAfterContentInitและใช้งานได้


@PhilipEnc ปัญหาของฉันเกี่ยวข้องกับการเปลี่ยนแปลงที่เกิดจากการเปลี่ยน DOM เมื่อ DOM จะเปลี่ยนวัตถุ QueryList (ที่มาจากคุณสมบัติ @ContentChildren) จะอัปเดตและภายในวิธีการอัปเดตที่เรียกว่ามันเปลี่ยนคุณสมบัติการผูกสองทาง สิ่งนี้สร้างปัญหาที่ฉันมี การห่อสิ่งที่เปลี่ยนเป็นสองคุณสมบัติด้วยsetTimeoutอย่างที่คุณแสดงด้านบนนั้นเป็นเคล็ดลับ ขอบคุณ!
kbpontius

1
ในกรณีของฉันฉันได้วางรหัสบางอย่างที่เปลี่ยนค่าของ array array primeng ใน ngAfterContentInit ฉันวางโค้ดใน ngOnInit และใช้งานได้
Vibhu

ngAfterContentCheckedทำงานที่นี่ในขณะที่ngAfterContentInitยังคงมีข้อผิดพลาด
ashubuntu

การใช้งาน ngAfterContentChecked แต่โครงการโหลดช้ามาก
Ghotekar Rahul

101

ข้อผิดพลาดนี้บ่งชี้ว่ามีปัญหาจริงในแอปพลิเคชันของคุณดังนั้นจึงเหมาะสมที่จะส่งข้อยกเว้น

ในdevModeการตรวจจับการเปลี่ยนแปลงจะเพิ่มเทิร์นเพิ่มเติมหลังจากการตรวจจับการเปลี่ยนแปลงทุกครั้งที่รันเพื่อตรวจสอบว่าโมเดลมีการเปลี่ยนแปลงหรือไม่

หากโมเดลมีการเปลี่ยนแปลงระหว่างการตรวจจับการเปลี่ยนแปลงปกติและการตรวจจับการเปลี่ยนแปลงเพิ่มเติมสิ่งนี้บ่งชี้ว่าทั้งสอง

  • การตรวจจับการเปลี่ยนแปลงตัวเองทำให้เกิดการเปลี่ยนแปลง
  • เมธอดหรือ getter ส่งคืนค่าต่างกันทุกครั้งที่ถูกเรียก

ซึ่งทั้งคู่ไม่ดีเพราะมันไม่ชัดเจนว่าจะดำเนินการต่ออย่างไรเพราะตัวแบบอาจจะไม่เสถียร

หาก Angular รันการตรวจจับการเปลี่ยนแปลงจนกว่าตัวแบบจะเสถียรมันอาจทำงานตลอดไป หาก Angular ไม่เรียกใช้การตรวจจับการเปลี่ยนแปลงมุมมองอาจไม่แสดงสถานะปัจจุบันของโมเดล

ดูเพิ่มเติมความแตกต่างระหว่างโหมดการผลิตและการพัฒนาใน Angular2 คืออะไร


4
ฉันจะหลีกเลี่ยงการเห็นข้อผิดพลาดนี้ในอนาคตได้อย่างไร มีวิธีอื่นที่ฉันต้องคิดเกี่ยวกับรหัสของฉันเพื่อให้แน่ใจว่าฉันจะไม่ทำผิดพลาดเหมือนกันหรือไม่?
Kevin LeStarge

25
โดยทั่วไปแล้วสิ่งนี้เกิดจากการเรียกกลับมาบางรอบเช่นngOnInitหรือngOnChangesปรับเปลี่ยนรูปแบบ (บางครั้งการเรียกกลับที่อนุญาตให้แก้ไขรูปแบบอื่น ๆ ไม่ได้ฉันจำไม่ได้ว่าตัวเองทำอย่างใดหรือไม่) อย่าผูกกับวิธีการหรือฟังก์ชั่นในมุมมองแทนที่จะผูกกับเขตข้อมูลและปรับปรุงเขตข้อมูลในตัวจัดการเหตุการณ์ หากคุณต้องผูกกับวิธีการตรวจสอบให้แน่ใจว่าพวกเขากลับเช่นอินสแตนซ์ค่าเดียวกันตราบใดที่ไม่มีการเปลี่ยนแปลงจริง การตรวจจับการเปลี่ยนแปลงจะเรียกวิธีการเหล่านี้เป็นจำนวนมาก
GünterZöchbauer

สำหรับทุกคนที่มาถึงที่นี่ซึ่งได้รับข้อผิดพลาดนี้โดยใช้ไลบรารี ngx-toaster นี่คือรายงานข้อผิดพลาด: github.com/scttcper/ngx-toastr/issues/160
rmcsharry

2
มันไม่จำเป็นต้องมีปัญหากับแอพ ความจริงที่ว่าการโทรchangeRef.detectChanges()เป็นวิธีการแก้ปัญหา / ระงับข้อผิดพลาดเป็นข้อพิสูจน์เรื่องนี้ มันเหมือนกับการปรับเปลี่ยนสถานะ$scope.$watch()ใน Angular 1
Kevin Beal

1
ฉันไม่ทราบว่า Angular 1 ดีเกินไป แต่การตรวจจับการเปลี่ยนแปลงใน Angular 2 ทำงานค่อนข้างแตกต่างกัน ของคุณถูกต้องว่ามันไม่จำเป็นต้องเป็นปัญหา แต่โดยปกติแล้วจะcdRef.detectChanges()มีความจำเป็นในบางกรณีที่แปลกและคุณควรดูอย่างระมัดระวังเมื่อคุณต้องการให้คุณเข้าใจว่าทำไม
GünterZöchbauer

83

ความเข้าใจมากมายเกิดขึ้นเมื่อฉันเข้าใจAngular Lifecycle Hooksและความสัมพันธ์กับการตรวจจับการเปลี่ยนแปลง

ฉันกำลังพยายามให้ Angular อัปเดตค่าสถานะโกลบอลที่โยงกับ*ngIfองค์ประกอบและฉันพยายามเปลี่ยนค่าสถานะนั้นภายในngOnInit()ตะขอวงจรชีวิตของส่วนประกอบอื่น

ตามเอกสารอธิบายวิธีการนี้เรียกว่าหลังจาก Angular ตรวจพบการเปลี่ยนแปลงแล้ว:

เรียกว่าหนึ่งครั้งหลังจาก ngOnChanges แรก ()

ดังนั้นการอัปเดตการตั้งค่าสถานะภายในngOnChanges()จะไม่เริ่มต้นการตรวจจับการเปลี่ยนแปลง จากนั้นเมื่อการตรวจจับการเปลี่ยนแปลงได้ถูกเรียกใช้อีกครั้งตามธรรมชาติค่าของธงจะเปลี่ยนไปและเกิดข้อผิดพลาดขึ้น

ในกรณีของฉันฉันเปลี่ยนสิ่งนี้:

constructor(private globalEventsService: GlobalEventsService) {

}

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

สำหรับสิ่งนี้:

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

ngOnInit() {

}

และมันแก้ไขปัญหา :)


3
ปัญหาของฉันคล้ายกัน ฉันทำผิดพลาดมาหลายชั่วโมงและกำหนดตัวแปรนอกฟังก์ชั่นและตัวสร้าง ngOnInit สิ่งนี้ได้รับการเปลี่ยนแปลงข้อมูลจากสิ่งที่สังเกตได้ซึ่งอยู่ในฟังก์ชั่นการทำให้มีพลัง ทำเช่นเดียวกันกับคุณเพื่อแก้ไขข้อผิดพลาด
ravo10

1
คล้ายกันมากรอบตัว แต่ฉันพยายามเลื่อน ( router.navigate) เมื่อโหลดไปยังส่วนหากมีอยู่ใน URL รหัสนี้ถูกวางไว้ในAfterViewInitตำแหน่งที่ฉันได้รับข้อผิดพลาดจากนั้นฉันก็ย้ายตามที่คุณพูดกับตัวสร้าง แต่มันก็ไม่เคารพชิ้นส่วน กำลังจะngOnInitแก้ไข :) ขอบคุณ!
Joel Balmer

จะเกิดอะไรขึ้นถ้า html ของฉันถูกผูกไว้กับเวลาที่กลับมาของ getter ในฐานะ "HH: MM" ผ่านการรับ ClockValue () {return DateTime.TimeAMPM (new Date ())} ในที่สุดมันจะเดินทางเมื่อนาทีเปลี่ยนไปในขณะที่การตรวจจับทำงานอยู่ แก้ไขปัญหานี้?
Meryan

กันที่นี่ นอกจากนี้ยังพบว่าการตัดเข้าสู่การsetInterval()ทำงานยังต้องใช้ไฟหลังจากรหัสเหตุการณ์ตลอดอายุการใช้งานอื่น ๆ
Rick Strahl

39

ปรับปรุง

ผมขอแนะนำให้เริ่มต้นด้วยการตอบสนองของตัวเองของ OPแรก: ต้องคิดเกี่ยวกับสิ่งที่สามารถทำได้ในconstructorVS ngOnChanges()สิ่งที่ควรจะทำใน

เป็นต้นฉบับ

นี่เป็นบันทึกด้านข้างมากกว่าคำตอบ แต่อาจช่วยใครซักคน ฉันสะดุดกับปัญหานี้เมื่อพยายามทำให้ปุ่มปรากฏขึ้นอยู่กับสถานะของแบบฟอร์ม:

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

เท่าที่ฉันรู้ไวยากรณ์นี้จะนำไปสู่ปุ่มที่ถูกเพิ่มและลบออกจาก DOM ตามเงื่อนไข ExpressionChangedAfterItHasBeenCheckedErrorซึ่งนำไปสู่การเปิดไป

การแก้ไขในกรณีของฉัน (แม้ว่าฉันจะไม่เรียกร้องให้เข้าใจถึงความแตกต่างอย่างเต็มที่) ก็ให้ใช้display: noneแทน:

<button [style.display]="form.pristine ? 'inline' : 'none'">Yo</button>

6
ความเข้าใจของฉันเกี่ยวกับความแตกต่างระหว่าง ngIf กับสไตล์คือ ngIf ไม่ได้รวม HTML ไว้ในหน้าจนกว่าเงื่อนไขจะเป็นจริงดังนั้นการลด "น้ำหนักหน้า" ลงเล็กน้อยในขณะที่เทคนิคลักษณะทำให้ HTML เป็นเสมอ ในหน้าและถูกซ่อนหรือแสดงตามค่าของ form.pristine
3785010

4
คุณอาจใช้[hidden]แทน[style.display]ส่วนverbose มาก :)
Philipp Meissner

2
ทำไมจะไม่ล่ะ. แม้ว่าดังที่กล่าวไว้โดย @Simon_Weaver ในความคิดเห็นอื่นในหน้านี้[hidden] จะไม่ได้มีพฤติกรรมเช่นเดียวกับdisplay: none
Arnaud P

1
ฉันแสดงปุ่มสองปุ่มที่แตกต่างกัน (ออกจากระบบ / เข้าสู่ระบบ) ด้วย * ngIf ในแต่ละปุ่มและนี่เป็นสาเหตุของปัญหา
GoTo

คอนสตรัคเป็นสถานที่ที่เหมาะสมสำหรับฉันเปิดตัวสแน็คบาร์วัสดุ
austin

31

มีคำตอบที่น่าสนใจ แต่ฉันไม่พบคำตอบที่ตรงกับความต้องการของฉันสิ่งที่ใกล้เคียงที่สุดจาก @ chittrang-mishra ซึ่งอ้างถึงฟังก์ชั่นเฉพาะอย่างหนึ่งเท่านั้น

ฉันไม่ต้องการใช้[hidden]เพื่อใช้ประโยชน์จากการ*ngIfไม่ได้เป็นส่วนหนึ่งของ DOM ดังนั้นฉันจึงพบวิธีแก้ไขปัญหาต่อไปนี้ซึ่งอาจไม่ดีที่สุดสำหรับทุกคนเนื่องจากมันระงับข้อผิดพลาดแทนที่จะแก้ไข แต่ในกรณีของฉันที่ฉันรู้ ผลลัพธ์สุดท้ายถูกต้องดูเหมือนว่าใช้ได้สำหรับแอปของฉัน

สิ่งที่ฉันทำคือการนำไปใช้AfterViewCheckedเพิ่มconstructor(private changeDetector : ChangeDetectorRef ) {}และจากนั้น

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

ฉันหวังว่านี่จะช่วยให้คนอื่น ๆ ช่วยฉันได้


3
นี่จะไม่ทริกเกอร์วนการตรวจจับการเปลี่ยนแปลงอนันต์หรือไม่ ฉันหมายความว่าคุณกำลังตรวจหาการเปลี่ยนแปลงหลังจากตรวจสอบแล้ว
Manuel Azar

@ManuelAzar เห็นได้ชัดว่ามันไม่ได้ นี่เป็นทางออกเดียวที่ใช้ได้สำหรับฉัน สุดท้ายเงียบไปหน่อยในคอนโซลของฉัน ฉันเหนื่อยกับการตรวจจับการเปลี่ยนแปลงที่ไม่เกี่ยวข้องเหล่านี้ทั้งหมด
Jeremy Thille

31

Angular รันการตรวจจับการเปลี่ยนแปลงและเมื่อพบว่าค่าบางอย่างที่ถูกส่งผ่านไปยังคอมโพเนนต์ชายด์ถูกเปลี่ยน Angular จะพ่นข้อผิดพลาด:

ExpressionChangedAfterItHasBeenCheckedError คลิกเพื่อเพิ่มเติม

เพื่อแก้ไขสิ่งนี้เราสามารถใช้AfterContentCheckedตะขอวงจรชีวิตและ

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

  constructor(
  private cdref: ChangeDetectorRef) { }

  ngAfterContentChecked() {

    this.cdref.detectChanges();

  }

แม้ว่าสิ่งนี้อาจแก้ไขปัญหานี้ไม่ได้กว้างขวางมากและ overkilling ซีดี?
Nicky

ฉันคิดว่านี่เป็นคำตอบเดียวที่แก้ไขข้อผิดพลาดนี้ที่เกิดจากการส่งค่าไปยังเด็ก ขอบคุณ!
java-addict301

@Nicky ใช่ ทุกครั้งที่คุณสัมผัสบนหน้าจอทุกที่จะมีการเรียก ngAfterContentChecked ()
Mert Mertce

25

ในกรณีของฉันฉันมีปัญหานี้ในไฟล์ข้อมูลจำเพาะของฉันในขณะที่ทำการทดสอบ

ฉันต้องเปลี่ยนngIf เป็น [hidden]

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

ถึง

<app-loading [hidden]="!isLoading"></app-loading>


2
ความแตกต่างที่นี่คือ*ngIfการเปลี่ยนแปลง DOM เพิ่มและลบองค์ประกอบจากหน้าในขณะที่[hidden]เปลี่ยนการมองเห็นของรายการในขณะที่ไม่ลบออกจาก DOM
Grungondola

5
แต่นี่ไม่ได้แก้ปัญหาจริง ...
ravo10

23

ทำตามขั้นตอนด้านล่าง:

1. ใช้ 'ChangeDetectorRef' โดยการนำเข้าจาก @ angular / core ดังต่อไปนี้:

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

2. ใช้งานใน constructor () ดังต่อไปนี้:

constructor(   private cdRef : ChangeDetectorRef  ) {}

3. เพิ่มวิธีการต่อไปนี้ในฟังก์ชั่นที่คุณกำลังโทรหาเหตุการณ์เช่นคลิกปุ่ม ดังนั้นจึงมีลักษณะเช่นนี้:

functionName() {   
    yourCode;  
    //add this line to get rid of the error  
    this.cdRef.detectChanges();     
}

23

ฉันใช้ ng2-carouselamos (Angular 8 & Bootstrap 4)

ด้านล่างแก้ไขปัญหาของฉัน:

ฉันทำอะไรลงไป:

1. implement AfterViewChecked,  
2. add constructor(private changeDetector : ChangeDetectorRef ) {} and then 
3. ngAfterViewChecked(){ this.changeDetector.detectChanges(); }

มันช่วยได้ ที่น่าตื่นตาตื่นใจ !!
Pathik Vejani

คุณบันทึกวันของฉัน ... ขอบคุณ!
omostan

19

ฉันพบปัญหาเดียวกันกับค่าที่เปลี่ยนแปลงในหนึ่งในอาร์เรย์ในองค์ประกอบของฉัน แต่แทนที่จะตรวจจับการเปลี่ยนแปลงการเปลี่ยนแปลงค่าฉันเปลี่ยนกลยุทธ์การตรวจจับการเปลี่ยนแปลงองค์ประกอบเป็นonPush(ซึ่งจะตรวจจับการเปลี่ยนแปลงการเปลี่ยนแปลงวัตถุและไม่ได้เป็นการเปลี่ยนค่า)

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

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

สิ่งนี้ดูเหมือนว่าจะทำงานสำหรับการเพิ่ม / ลบออกจากการควบคุมแบบไดนามิก .. มีข้อบกพร่องหรือไม่?
Ricardo Saracino

ทำงานเหมือนจับใจในสถานการณ์ที่ฉันมีอยู่ในมือขอบคุณ! องค์ประกอบถูกผูกไว้กับวัตถุ "ส่วนกลาง" ซึ่งถูกเปลี่ยนแปลงที่อื่นและทำให้เกิดข้อผิดพลาดเกิดขึ้น คอมโพเนนต์นี้มีตัวจัดการการอัปเดตแล้วเมื่อวัตถุที่ถูกผูกไว้ได้รับการปรับปรุงตัวจัดการเหตุการณ์นี้เรียกใช้ changeDetectorRef.detectChanges () ร่วมกับ ChangeDetectionStrategy.On
Bernoulli ไอที

@RicardoSaracino คุณพบข้อบกพร่องหรือไม่? ฉันสงสัยในสิ่งเดียวกัน ฉันรู้ว่าการตรวจจับการเปลี่ยนแปลง OnPush ทำงานอย่างไร แต่สงสัยว่ามี gotcha ที่บางทีฉันอาจหายไป ฉันไม่ต้องการวนกลับ
mtpultz

@RicardoSaracino, ใช่มันมีข้อเสียบางอย่างคุณสามารถดูลิงค์รายละเอียดนี้blog.angular-university.io/onpush-change-detection-how-it-works
Dheeraj

@BernoulliIT ขอบคุณฉันดีใจที่ได้ผลกับคุณ
Dheeraj

17

อ้างถึงบทความhttps://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4

ดังนั้นกลไกที่อยู่เบื้องหลังการตรวจจับการเปลี่ยนแปลงจึงใช้งานได้จริงในลักษณะที่ทั้งการตรวจจับการเปลี่ยนแปลงและการตรวจสอบการย่อยถูกทำขึ้นพร้อมกัน ซึ่งหมายความว่าหากเราอัปเดตคุณสมบัติแบบอะซิงโครนัสค่าจะไม่ได้รับการอัปเดตเมื่อลูปการตรวจสอบทำงานและเราจะไม่ได้รับExpressionChanged...ข้อผิดพลาด เหตุผลที่เราได้รับข้อผิดพลาดนี้คือในระหว่างกระบวนการตรวจสอบ Angular เห็นค่าที่แตกต่างจากนั้นสิ่งที่บันทึกไว้ในระหว่างขั้นตอนการตรวจจับการเปลี่ยนแปลง ดังนั้นเพื่อหลีกเลี่ยงที่ ....

1) ใช้ changeDetectorRef

2) ใช้ setTimeOut สิ่งนี้จะรันโค้ดของคุณใน VM อื่นเป็นงานแมโคร Angular จะไม่เห็นการเปลี่ยนแปลงเหล่านี้ในระหว่างกระบวนการตรวจสอบและคุณจะไม่ได้รับข้อผิดพลาดนั้น

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

3) ถ้าคุณต้องการรันโค้ดของคุณบนการใช้ VM เช่นเดียวกัน

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

สิ่งนี้จะสร้างงานแบบจุลภาค คิวงานไมโครถูกประมวลผลหลังจากรหัสซิงโครนัสปัจจุบันเสร็จสิ้นการดำเนินการดังนั้นการปรับปรุงคุณสมบัติจะเกิดขึ้นหลังจากขั้นตอนการตรวจสอบ


คุณสามารถใช้ตัวเลือก # 3 กับการแสดงออกสไตล์ได้หรือไม่? ฉันมีการแสดงออกสไตล์สำหรับความสูงที่ควรประเมินครั้งสุดท้ายเพราะมันขึ้นอยู่กับเนื้อหาที่ฉีด
N-ate

1
ขออภัยที่เห็นความคิดเห็นของคุณใช่ฉันไม่เห็นเหตุผลใด ๆ ดังนั้นควรเปลี่ยนสไตล์ด้วยเช่นกัน
อื่น ๆ

4

@HostBinding อาจเป็นแหล่งที่มาของข้อผิดพลาดนี้ที่สับสน

ตัวอย่างเช่นสมมติว่าคุณมีการเชื่อมโยงโฮสต์ต่อไปนี้ในส่วนประกอบ

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

เพื่อความง่ายสมมติว่ามีการอัปเดตคุณสมบัตินี้ผ่านคุณสมบัติอินพุตต่อไปนี้:

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

ในองค์ประกอบหลักคุณจะตั้งโปรแกรมโดยอัตโนมัติ ngAfterViewInit

@ViewChild(ImageCarousel) carousel: ImageCarousel;

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

นี่คือสิ่งที่เกิดขึ้น:

  • องค์ประกอบหลักของคุณถูกสร้างขึ้น
  • คอมโพเนนต์ ImageCarousel ถูกสร้างและกำหนดให้carousel(ผ่าน ViewChild)
  • เราไม่สามารถเข้าถึงcarouselจนกว่าngAfterViewInit()(มันจะเป็นโมฆะ)
  • เรากำหนดค่าซึ่งกำหนด style_groupBG = 'red'
  • ชุดนี้จะเปิดbackground: redในองค์ประกอบ ImageCarousel โฮสต์
  • ส่วนประกอบนี้เป็น 'องค์ประกอบ' ขององค์ประกอบหลักของคุณดังนั้นเมื่อตรวจสอบการเปลี่ยนแปลงพบว่ามีการเปลี่ยนแปลงcarousel.style.backgroundและไม่ฉลาดพอที่จะรู้ว่านี่ไม่ใช่ปัญหาดังนั้นจึงเกิดข้อยกเว้นขึ้น

ทางออกหนึ่งคือการแนะนำ ImageCarousel ของ wrapper อีกอันหนึ่งและตั้งค่าสีพื้นหลังบนนั้น แต่คุณจะไม่ได้รับประโยชน์จากการใช้HostBinding(เช่นอนุญาตให้ผู้ปกครองควบคุมขอบเขตทั้งหมดของวัตถุ)

ทางออกที่ดีกว่าในองค์ประกอบหลักคือการเพิ่ม detectChanges () หลังจากตั้งค่า

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

นี่อาจดูเหมือนชัดเจนมากเช่นนี้และคล้ายกับคำตอบอื่น ๆ แต่มีความแตกต่างเล็กน้อย

พิจารณากรณีที่คุณไม่ได้เพิ่ม@HostBindingจนกระทั่งในภายหลังในระหว่างการพัฒนา ทันใดนั้นคุณได้รับข้อผิดพลาดนี้และดูเหมือนจะไม่สมเหตุสมผล


2

นี่คือความคิดของฉันในสิ่งที่เกิดขึ้น ฉันยังไม่ได้อ่านเอกสาร แต่แน่ใจว่านี่เป็นส่วนหนึ่งของสาเหตุที่ข้อผิดพลาดปรากฏขึ้น

*ngIf="isProcessing()" 

เมื่อใช้ * ngIf จะเป็นการเปลี่ยนแปลง DOM โดยการเพิ่มหรือลบองค์ประกอบทุกครั้งที่มีการเปลี่ยนแปลงเงื่อนไข ดังนั้นหากเงื่อนไขเปลี่ยนไปก่อนที่มันจะแสดงผลไปยังมุมมอง (ซึ่งเป็นไปได้สูงในโลกของ Angular) ข้อผิดพลาดจะถูกโยน ดูคำอธิบายที่นี่ระหว่างการพัฒนาและโหมดการผลิต

[hidden]="isProcessing()"

เมื่อใช้[hidden]มันจะไม่เปลี่ยนทางกายภาพDOMแต่เพียงซ่อนelementจากมุมมองส่วนใหญ่มีแนวโน้มที่จะใช้CSSที่ด้านหลัง องค์ประกอบยังคงอยู่ใน DOM แต่ไม่สามารถมองเห็นได้ขึ้นอยู่กับค่าของเงื่อนไข [hidden]นั่นคือเหตุผลที่ผิดพลาดจะไม่เกิดขึ้นเมื่อใช้


หากisProcessing()ทำสิ่งเดียวกันคุณต้องใช้!isProcessing()สำหรับ[hidden]
Matthieu Charbonnier

hiddenไม่ได้ "ใช้ CSS ที่ด้านหลัง" มันเป็นคุณสมบัติ HTML ปกติ developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/…
Lazar Ljubenović

1

สำหรับปัญหาของฉันฉันกำลังอ่านgithub - "ExpressionChangedAfterItHasBeenCheckedError เมื่อเปลี่ยนค่าองค์ประกอบ 'non model' ใน afterViewInit" และตัดสินใจที่จะเพิ่ม ngModel

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

มันแก้ไขปัญหาของฉันฉันหวังว่าจะช่วยใครบางคน


1
เว็บไซต์บอกว่าจะเพิ่มngModelที่ไหน และคุณได้โปรดอธิบายเพิ่มเติมว่าทำไมสิ่งนี้ถึงมีประโยชน์?
Peter Wippermann

เมื่อฉันติดตามปัญหานี้มันทำให้ฉันตรวจสอบลิงก์ หลังจากอ่านบทความแล้วฉันได้เพิ่มคุณสมบัติและแก้ไขปัญหาของฉันได้ มันจะมีประโยชน์ถ้ามีคนพบปัญหาเดียวกัน
Demodave

1

เคล็ดลับการแก้จุดบกพร่อง

ข้อผิดพลาดนี้อาจทำให้เกิดความสับสนและมันง่ายที่จะทำการสันนิษฐานผิด ๆ ว่าเกิดขึ้นเมื่อไร ฉันพบว่ามีประโยชน์ในการเพิ่มคำสั่งการแก้ไขข้อบกพร่องจำนวนมากเช่นนี้ตลอดองค์ประกอบที่ได้รับผลกระทบในสถานที่ที่เหมาะสม สิ่งนี้ช่วยให้เข้าใจการไหล

ในพาเรนต์วางข้อความแบบนี้ (สตริงที่แน่นอน 'EXPRESSIONCHANGED' มีความสำคัญ) แต่นอกเหนือจากนั้นสิ่งเหล่านี้เป็นเพียงตัวอย่าง:

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

ในการโทรกลับเด็ก / บริการ / จับเวลา:

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

หากคุณเรียกใช้detectChangesเพิ่มบันทึกด้วยตนเองเช่นกัน:

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

จากนั้นในตัวแก้ไขข้อบกพร่องของ Chrome เพียงกรองตาม 'EXPRESSIONCHANGES' สิ่งนี้จะแสดงให้คุณทราบถึงการไหลและลำดับของทุกสิ่งที่เกิดขึ้นอย่างแม่นยำและตรงจุดที่ Angular พ่นข้อผิดพลาด

ป้อนคำอธิบายรูปภาพที่นี่

คุณสามารถคลิกที่ลิงค์สีเทาเพื่อใส่จุดพัก

สิ่งอื่นที่ต้องระวังหากคุณมีคุณสมบัติชื่อคล้ายกันตลอดทั้งแอปพลิเคชันของคุณ (เช่นstyle.background) ตรวจสอบให้แน่ใจว่าคุณทำการดีบั๊กที่คุณคิดว่าคุณ - โดยการตั้งค่าให้เป็นค่าสีที่ไม่ชัดเจน


1

ในกรณีของฉันฉันมีคุณสมบัติ async LoadingServiceพร้อมกับ BehavioralSubjectisLoading

การใช้รูปแบบ[ซ่อน]ทำงานได้ แต่* ngIfล้มเหลว

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

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

1

วิธีแก้ปัญหาที่ใช้งานได้สำหรับฉันโดยใช้ rxjs

import { startWith, tap, delay } from 'rxjs/operators';

// Data field used to populate on the html
dataSource: any;

....

ngAfterViewInit() {
  this.yourAsyncData.
      .pipe(
          startWith(null),
          delay(0),
          tap((res) => this.dataSource = res)
      ).subscribe();
}

รหัสปัญหาคืออะไร ทางออกคืออะไร
mkb

สวัสดี @mkb ปัญหาExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.เกิดขึ้นเมื่อมีการเปลี่ยนแปลงค่าเมื่อมีการเปลี่ยนแปลง DOM
Sandeep K Nair

สวัสดีฉันหมายถึงสิ่งที่คุณทำที่นี่เพื่อเอาชนะปัญหา คุณไม่ได้ใช้ rxjs เลยก่อนหรือเพิ่มความล่าช้า () หรือเพิ่ม startWith ()? ฉันใช้ rxjs กับ rxjs หลายวิธีแล้วแต่ยังคงได้รับข้อผิดพลาดฉันหวังว่าจะแก้ปัญหา mistery :(
mkb

การเพิ่มdelayทำให้ข้อผิดพลาดหายไป setTimeoutมันเป็นผลงานที่คล้ายกับ
Lazar Ljubenović

1

ฉันมีข้อผิดพลาดแบบนี้ใน Ionic3 (ซึ่งใช้ Angular 4 เป็นส่วนหนึ่งของ stack เทคโนโลยี)

สำหรับฉันมันกำลังทำสิ่งนี้:

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

ดังนั้นฉันจึงพยายามเปลี่ยนประเภทของไอคอนไอออนจาก a pinเป็น a remove-circleตามโหมดที่หน้าจอใช้งาน

ฉันเดาว่าฉันจะต้องเพิ่ม*ngIfแทน


1

ปัญหาของฉันปรากฏเมื่อฉันเพิ่ม*ngIfแต่นั่นไม่ใช่สาเหตุ ข้อผิดพลาดเกิดจากการเปลี่ยนรูปแบบใน{{}}แท็กจากนั้นพยายามที่จะแสดงรูปแบบการเปลี่ยนแปลงใน*ngIfคำสั่งในภายหลัง นี่คือตัวอย่าง:

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

เพื่อแก้ไขปัญหาฉันเปลี่ยนสถานที่ซึ่งฉันโทรchangeMyModelValue()ไปยังสถานที่ที่เหมาะสมกว่า

ในสถานการณ์ของฉันฉันต้องการchangeMyModelValue()เรียกว่าเมื่อใดก็ตามที่องค์ประกอบลูกเปลี่ยนแปลงข้อมูล ฉันต้องสร้างและปล่อยเหตุการณ์ในองค์ประกอบลูกเพื่อให้ผู้ปกครองสามารถจัดการได้ (โดยการโทรchangeMyModelValue()ดูhttps://angular.io/guide/component-interaction#parent-listens-for-child-event


0

ฉันหวังว่าสิ่งนี้จะช่วยให้ใครบางคนมาที่นี่: เราทำการบริการngOnInitในลักษณะดังต่อไปนี้และใช้ตัวแปรdisplayMainเพื่อควบคุมการติดตั้งองค์ประกอบกับ DOM

component.ts

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

และ component.html

<div *ngIf="displayMain"> <!-- This is the Root Element -->
 <!-- All the HTML Goes here -->
</div>

0

ฉันได้รับข้อผิดพลาดนี้เพราะฉันใช้ตัวแปรใน component.html ซึ่งไม่ได้ประกาศใน component.ts เมื่อฉันลบส่วนใน HTML ข้อผิดพลาดนี้ก็หายไป


0

ฉันได้รับข้อผิดพลาดนี้เนื่องจากฉันกำลังส่งการกระทำของ redux ใน modal และ modal นั้นไม่ได้เปิดในเวลานั้น ฉันกำลังจัดส่งแอ็คชั่นขณะที่องค์ประกอบโมดัลรับอินพุต ดังนั้นฉันจึงวาง setTimeout ที่นั่นเพื่อให้แน่ใจว่าคำกริยานั้นเปิดอยู่


0

สำหรับทุกคนที่ดิ้นรนกับสิ่งนี้ ต่อไปนี้เป็นวิธีแก้ไขข้อผิดพลาดอย่างถูกต้อง: https://blog.angular-university.io/angular-debugging/

ในกรณีของฉันฉันได้กำจัดข้อผิดพลาดนี้โดยใช้การแฮ็ก [ซ่อน] แทนการใช้ * ngIf ...

แต่ลิงก์ที่ฉันให้ไว้ทำให้ฉันสามารถค้นหาTHE GUILTY * ngIf :)

สนุก.


การใช้hiddenแทนที่จะngIfเป็นแฮ็คและไม่ได้แก้ไขปัญหาหลักของปัญหาเลย คุณกำลังปกปิดปัญหาอยู่
Lazar Ljubenović

-2

โซลูชัน ... บริการและ rxjs ... ตัวปล่อยเหตุการณ์และการเชื่อมโยงคุณสมบัติทั้งสองใช้ rxjs .. คุณดีกว่าในการใช้งานด้วยตนเองควบคุมได้ดีขึ้นง่ายต่อการดีบัก โปรดจำไว้ว่าตัวปล่อยเหตุการณ์กำลังใช้ rxjs เพียงสร้างบริการและภายในสิ่งที่สังเกตได้ให้แต่ละส่วนประกอบสมัครสมาชิกผู้สังเกตการณ์และผ่านค่าใหม่หรือค่า cosume ตามต้องการ


1
ไม่เพียงแค่ไม่ตอบคำถามเท่านั้น แต่ยังเป็นคำแนะนำที่น่ากลัวอีกด้วย พวกคุณโปรดอย่านำ rxjs มาใช้ใหม่ด้วยตัวเองเพียงเพราะคุณได้รับข้อผิดพลาดจากซีดีของ Angular :)
Lazar Ljubenović
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.