Expression ___ has changed after it was checked

后端 未结 17 1872
太阳男子
太阳男子 2020-11-22 05:53

Why is the component in this simple plunk

@Component({
  selector: \'my-app\',
  template: `
I\'m {{message}}
`, }) export class App {
相关标签:
17条回答
  • 2020-11-22 06:09

    This error is coming because existing value is getting updated immediately after getting initialized. So if you will update new value after existing value is rendered in DOM, Then it will work fine.Like mentioned in this article Angular Debugging "Expression has changed after it was checked"

    for example you can use

    ngOnInit() {
        setTimeout(() => {
          //code for your new value.
        });
    

    }

    or

    ngAfterViewInit() {
      this.paginator.page
          .pipe(
              startWith(null),
              delay(0),
              tap(() => this.dataSource.loadLessons(...))
          ).subscribe();
    }
    

    As you can see i have not mentioned time in setTimeout method. As it is browser provided API, not a JavaScript API, So this will run seperately in browser stack and will wait till call stack items are finished.

    How browser API envokes concept is explained by Philip Roberts in one of Youtube video(What the hack is event loop?).

    0 讨论(0)
  • 2020-11-22 06:10

    In my case, it happened with a p-radioButton. The problem was that I was using the name attribute (which wasn't needed) alongside the formControlName attribute like this:

    <p-radioButton formControlName="isApplicant" name="isapplicant" value="T" label="Yes"></p-radioButton>
    <p-radioButton formControlName="isApplicant" name="isapplicant" value="T" label="No"></p-radioButton>
    

    I also had the initial value "T" bound to the isApplicant form control like this:

    isApplicant: ["T"]
    

    I fixed the problem by removing the name attributes in the radio buttons. Also, because the 2 radio buttons have the same value (T) which is wrong in my case, simply changing one of then to another value (say F) also fixed the issue.

    0 讨论(0)
  • 2020-11-22 06:12

    I think a most simple solution would be as follows:

    1. Make one implementation of assigning a value to some variable i.e. via function or setter.
    2. Create a class variable (static working: boolean) in the class where this function exists and every time you call the function, simply make it true whichever you like. Within the function, if the value of working is true, then simply return right away without doing anything. else, perform the task you want. Make sure to change this variable to false once the task is completed i.e. at the end of the line of codes or within the subscribe method when you are done assigning values!
    0 讨论(0)
  • 2020-11-22 06:13

    The article Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError error explains the behavior in great details.

    The problem with you setup is that ngAfterViewInit lifecycle hook is executed after change detection processed DOM updates. And you're effectively changing the property that is used in the template in this hook which means that DOM needs to be re-rendered:

      ngAfterViewInit() {
        this.message = 'all done loading :)'; // needs to be rendered the DOM
      }
    

    and this will require another change detection cycle and Angular by design only runs one digest cycle.

    You basically have two alternatives how to fix it:

    • update the property asynchronously either using setTimeout, Promise.then or asynchronous observable referenced in the template

    • perform the property update in a hook before the DOM update - ngOnInit, ngDoCheck, ngAfterContentInit, ngAfterContentChecked.

    0 讨论(0)
  • 2020-11-22 06:13

    You can also create a timer using the rxjs Observable.timer function, and then update the message in your subscription:                    

    Observable.timer(1).subscribe(()=> this.updateMessage());
    
    0 讨论(0)
  • 2020-11-22 06:17

    As stated by drewmoore, the proper solution in this case is to manually trigger change detection for the current component. This is done using the detectChanges() method of the ChangeDetectorRef object (imported from angular2/core), or its markForCheck() method, which also makes any parent components update. Relevant example:

    import { Component, ChangeDetectorRef, AfterViewInit } from 'angular2/core'
    
    @Component({
      selector: 'my-app',
      template: `<div>I'm {{message}} </div>`,
    })
    export class App implements AfterViewInit {
      message: string = 'loading :(';
    
      constructor(private cdr: ChangeDetectorRef) {}
    
      ngAfterViewInit() {
        this.message = 'all done loading :)'
        this.cdr.detectChanges();
      }
    
    }
    

    Here are also Plunkers demonstrating the ngOnInit, setTimeout, and enableProdMode approaches just in case.

    0 讨论(0)
提交回复
热议问题