我的组件具有依赖于当前日期时间的样式。在我的组件中,我有如下函数。

  private fontColor( dto : Dto ) : string {
    // date d'exécution du dto
    let dtoDate : Date = new Date( dto.LastExecution );

    (...)

    let color =  "hsl( " + hue + ", 80%, " + (maxLigness - lightnessAmp) + "%)";

    return color;
  }

lightnessAmp从当前datetime开始计算。如果dtoDate在过去24小时内,则颜色会发生变化。

准确的错误如下:

表达式在检查后发生了变化。先前值:'hsl(123, 80%, 49%)'。当前值:'hsl(123, 80%, 48%)'

我知道异常只出现在开发模式的值被检查的时刻。如果检查值与更新值不同,则抛出异常。

因此,我尝试在以下钩子方法中更新每个生命周期的当前datetime,以防止异常:

  ngAfterViewChecked()
  {
    console.log( "! changement de la date du composant !" );
    this.dateNow = new Date();
  }

...但是没有成功。


当前回答

使用默认表单值可避免此错误。

而不是使用在ngAfterViewInit()中应用detectChanges()的接受答案(这也解决了我的情况下的错误),我决定为动态所需的表单字段保存一个默认值,这样当表单稍后更新时,如果用户决定更改表单上的选项,将触发新的必需字段(并导致提交按钮被禁用),它的有效性不会改变。

这在我的组件中节省了一小部分代码,在我的案例中,错误完全避免了。

其他回答

使用默认表单值可避免此错误。

而不是使用在ngAfterViewInit()中应用detectChanges()的接受答案(这也解决了我的情况下的错误),我决定为动态所需的表单字段保存一个默认值,这样当表单稍后更新时,如果用户决定更改表单上的选项,将触发新的必需字段(并导致提交按钮被禁用),它的有效性不会改变。

这在我的组件中节省了一小部分代码,在我的案例中,错误完全避免了。

在我们的例子中,我们通过在组件中添加changeDetection来FIXED,并调用ngAfterContentChecked中的detectChanges(),代码如下所示

@Component({
  selector: 'app-spinner',
  templateUrl: './spinner.component.html',
  styleUrls: ['./spinner.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class SpinnerComponent implements OnInit, OnDestroy, AfterContentChecked {

  show = false;

  private subscription: Subscription;

  constructor(private spinnerService: SpinnerService, private changeDedectionRef: ChangeDetectorRef) { }

  ngOnInit() {
    this.subscription = this.spinnerService.spinnerState
      .subscribe((state: SpinnerState) => {
        this.show = state.show;
      });
  }

  ngAfterContentChecked(): void {
      this.changeDedectionRef.detectChanges();
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

}

你可以使用µsetTimeout()在javascript事件循环的下一个宏任务队列中运行属性的设置。 但是在这种情况下,使用detectChanges()就足够了。出现这种错误的原因有很多:

本文解释了产生此错误的所有原因以及此错误的所有解决方案:https://blog.simplified.courses/angular-expression-changed-after-it-has-been-checked-error/

在更改之后显式地运行更改检测:

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

constructor(private cdRef:ChangeDetectorRef) {}

ngAfterViewChecked()
{
  console.log( "! changement de la date du composant !" );
  this.dateNow = new Date();
  this.cdRef.detectChanges();
}

博士TL;

ngAfterViewInit() {
    setTimeout(() => {
        this.dateNow = new Date();
    });
}

虽然这是一种变通方法,但有时真的很难以更好的方式解决这个问题,所以如果您使用这种方法,请不要责怪自己。没关系。

示例:初始问题[链接],用setTimeout()解决[链接]


如何避免

一般来说,这个错误通常发生在你添加ngAfterViewInit之后(甚至在父/子组件中)。第一个问题是问你自己,我能没有ngAfterViewInit吗?也许你把代码移到某个地方(ngAfterViewChecked可能是另一种选择)。

例如:[链接]


ngAfterViewInit中影响DOM的async也可能导致这个。也可以通过setTimeout或在管道中添加delay(0)操作符来解决:

ngAfterViewInit() {
  this.foo$
    .pipe(delay(0)) //"delay" here is an alternative to setTimeout()
    .subscribe();
}

例如:[链接]


好的阅读

关于如何调试以及为什么会发生这种情况的好文章:链接