为什么这个组件在这个简单的砰砰声中

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App {
  message:string = 'loading :(';

  ngAfterViewInit() {
    this.updateMessage();
  }

  updateMessage(){
    this.message = 'all done loading :)'
  }
}

扔:

例外:表达式'I'm {{message}} in App@0:5'在被检查后发生了变化。之前的值:'I'm loading:('。当前值:'I'm all done loading:)' in [I'm {{message}} in App@0:5]

当我所做的一切都是更新一个简单的绑定时,我的视图被启动?


当前回答

不错的答案。然而,在我看来,当我使用: ChangeDetectorRef和AfterViewInit, Angular会进入一些额外的渲染周期,如果我的HTML代码设计得不是很小心,或者需要在TS代码中调用几个依赖于刷新的函数,我就会得到额外的视图渲染调用,因此会有额外的处理。

这里有一个我喜欢使用的解决方案,因为我不需要担心任何这些,它在编程上非常简单,并且不需要我或系统太多额外的东西。每当Angular给我带来“Expression has changed after it was checked”这种臭名昭著的错误时,我使用它都没有问题。

我有这个小的public/exported函数,它只是通过一个零延迟承诺传递我的值。这样做的目的是迫使JavaScript/JS进入另一个后台周期,从而将值更新分离到下一个处理周期,并-防止错误。(请注意,JS的周期与Angular HTML的视图呈现周期不同,处理强度更低)。

export async function delayValue(v: any, timeOutMs: number = 0): Promise<any> {
    return new Promise((resolve) => {
        setTimeout(() => {
          resolve(v);
        }, timeOutMs);
      });
}

现在,当我需要防止错误时,我简单地做:

this.myClassValue = await delayValue(newValue);

这只是一行代码。由于timeOutMs = 0的值,实际上没有明显的延迟。

下面是一个典型的场景:

myObservable$.subscribe(newValue  = {
    ...                // WHEN NEW VALUE ARRIVES FROM NOTIFIER(S)
    this.handleSubscribedValues(newValue);
    ...
});
                      // THIS MAY GIVE YOU THE ERROR !
private handleSubscribedValues(newValue) {
    this.myClassValue = newValue;
}
                      // SO, USE THIS INSTEAD TO AVOID THE ERROR
private async handleSubscribedValues(newValue) {
    this.myClassValue = await delayValue(newValue);
}

你也可以使用delayValue()函数和一些延迟/超时值,如果你需要等待一些事情发生,例如给用户几秒钟。

希望这对你们中的一些人有用。

其他回答

在使用数据表时,我得到了类似的错误。当你在另一个*ngFor数据表中使用*ngFor时,会在它拦截角度变化周期时抛出这个错误。因此,不要在数据表内部使用数据表,而是使用一个常规的表或替换mf。带有数组名的数据。这很好。

你也可以把你对updateMessage()的调用放在ngOnInt()-方法中,至少它对我有用

ngOnInit() {
    this.updateMessage();
}

在RC1中,这不会触发异常

我不能评论@Biranchi的帖子,因为我没有足够的声誉,但它为我解决了这个问题。

有一件事要注意! 如果添加changeDetection: ChangeDetectionStrategy。OnPush在组件上没有工作,它是一个子组件(哑组件)尝试添加到父组件也。

这修复了bug,但我想知道这有什么副作用。

出现此错误是因为现有值在初始化后立即更新。所以如果你将更新新值后,现有的值在DOM中呈现,那么它将工作得很好。就像本文中提到的,Angular调试"表达式被检查后发生了变化"

比如你可以用

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

}

or

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

正如你所看到的,我没有在setTimeout方法中提到时间。因为它是浏览器提供的API,而不是JavaScript API,所以这将在浏览器堆栈中单独运行,并将等待调用堆栈项完成。

Philip Roberts在Youtube的一个视频中解释了浏览器API如何调用这个概念(什么是事件循环?)

它抛出一个错误,因为你的代码在ngAfterViewInit()被调用时被更新了。意思是当ngAfterViewInit发生时你的初始值被改变了,如果你在ngAfterContentInit()中调用它,那么它不会抛出一个错误。

ngAfterContentInit() {
    this.updateMessage();
}