ExpressionChangedAfterItHasBeenCheckedError Explained

前端 未结 26 1522
慢半拍i
慢半拍i 2020-11-22 14:46

Please explain to me why I keep getting this error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.

Obviously,

相关标签:
26条回答
  • 2020-11-22 15:38

    A solution that worked for me using 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();
    }
    
    0 讨论(0)
  • 2020-11-22 15:41

    My issue was manifest when I added *ngIf but that wasn't the cause. The error was caused by changing the model in {{}} tags then trying to display the changed model in the *ngIf statement later on. Here's an example:

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

    To fix the issue, I changed where I called changeMyModelValue() to a place that made more sense.

    In my situation I wanted changeMyModelValue() called whenever a child component changed the data. This required I create and emit an event in the child component so the parent could handle it (by calling changeMyModelValue(). see https://angular.io/guide/component-interaction#parent-listens-for-child-event

    0 讨论(0)
  • 2020-11-22 15:41

    I was struggling with this issue for a while, primarily while running tests in my dev environment. I was able to resolve the issue with a simple setTimeout around a service call response!

    0 讨论(0)
  • 2020-11-22 15:42

    Tried most of the solutions suggested above. Only this worked for me in this scenario. I was using *ngIf to toggle angular material's indeterminate progressive bar based on api calls and it was throwing ExpressionChangedAfterItHasBeenCheckedError.

    In the component in question:

    constructor(
        private ngZone: NgZone,
        private changeDetectorRef: ChangeDetectorRef,
    ) {}
    
    ngOnInit() {
        this.ngZone.runOutsideAngular(() => {
            this.appService.appLoader$.subscribe(value => {
                this.loading = value;
                this.changeDetectorRef.detectChanges();
            });
        });
    }
    

    The trick is to bypass angular component's change detection using ngzone.

    PS: Not sure if this is an elegant solution but using AfterContentChecked and AfterViewChecked lifecycle hook is bound to raise performance issues as your application gets bigger as it is triggered numerous times.

    0 讨论(0)
  • 2020-11-22 15:44

    Debugging tips

    This error can be quite confusing, and it's easy to make a wrong assumption about exactly when it's occuring. I find it helpful to add a lot of debugging statements like this throughout the affected components in the appropriate places. This helps understand the flow.

    In the parent put statements like this (the exact string 'EXPRESSIONCHANGED' is important), but other than that these are just examples:

        console.log('EXPRESSIONCHANGED - HomePageComponent: constructor');
        console.log('EXPRESSIONCHANGED - HomePageComponent: setting config', newConfig);
        console.log('EXPRESSIONCHANGED - HomePageComponent: setting config ok');
        console.log('EXPRESSIONCHANGED - HomePageComponent: running detectchanges');
    

    In the child / services / timer callbacks:

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

    If you run detectChanges manually add logging for that too:

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

    Then in Chrome debugger just filter by 'EXPRESSIONCHANGES'. This will show you exactly the flow and order of everything that gets set, and also exactly at what point Angular throws the error.

    You can also click on the gray links to put breakpoints in.

    Another thing to watch out if you have similarly named properties throughout your application (such as style.background) make sure you're debugging the one you think you - by setting it to an obscure color value.

    0 讨论(0)
  • 2020-11-22 15:45

    Angular runs change detection and when it finds that some values which has been passed to the child component have been changed, Angular throws the following error:

    ExpressionChangedAfterItHasBeenCheckedError click for more

    In order to correct this we can use the AfterContentChecked life cycle hook and

    import { ChangeDetectorRef, AfterContentChecked} from '@angular/core';
    
      constructor(
      private cdref: ChangeDetectorRef) { }
    
      ngAfterContentChecked() {
    
        this.cdref.detectChanges();
    
      }
    
    0 讨论(0)
提交回复
热议问题