首页 > 解决方案 > 面对“ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked”来显示/隐藏加载器

问题描述

我在父组件(AppComponent)中有一个通用加载器以及router-outlet(加载子组件)。在子组件中,我们更改值以分别在调用 API 之前和之后显示和隐藏加载器。因此发生此错误。

例如这是我的装载机app.cpmponent.html

<p [ngClass]="(overlappingProgress | async) ? 'display-block' : 'display-none'">holaaaaaaaaaaaaaaa</p>

overlappingProgress是一种observable通过async管道解析的变量。

在子组件中,我有一个需要来自服务器的一些数据的表单。因此,在ngOnInit子组件内部,我ngrx按以下顺序调度了一些操作(正如我们正在使用的那样) -

  1. showOverlappingProgress() // 它使 reducer 中的加载属性为 true
  2. 获取数据()
  3. hideOverlappingProgress() // 它使 reducer 中的加载属性为 false

因此,app.component.ts为了检测该更改,我会听取更改以更新显示/隐藏加载程序。

this.overlappingProgress = this.store.select(s => s.app.overlappingProgress);

我有一些想法,它将先前/旧值与第一个摘要周期的值进行比较,但该值在摘要周期内发生变化,因此,当它运行第二个摘要周期时,值不匹配,因此发生此错误。

我在stackblitz中使用更简单的版本来模拟这种情况。

我找到了一些建议使用的解决方案setTimeout()or this.cdr.detectChanges()

个人觉得不太setTimeout()行。此外,手动触发detectchanges可能会做很多额外的工作,因为所有子组件都在起诉这个加载器,每次它都会触发和额外的变化检测。

因此,我在这里就在这种情况下应该是最佳解决方案获得一些建议。

标签: angularangular-changedetection

解决方案


它有父子关系。子组件更改在父组件的表达式中使用的父组件的值。

为了解决这个问题,我稍微改变了父子结构。我创建了一个名为的新模块FfLoadingIndicatorComponentModule,它有一个名为FfLoadingIndicatorComponent. 在该组件中,我订阅了它显示的更改,分别隐藏了加载器。最后,我将该模块导入AppModule以使用来自应用程序组件的新创建的加载指示器组件。

这里主要是单独的组件及其与应用程序组件的关系。让我们看看代码。代码示例:

ff-loading-indicator.component.ts

@Component({
    selector: 'ff-loading-indicator',
    templateUrl: './ff-loading-indicator.component.html',
    styleUrls: ['./ff-loading-indicator.component.scss']
})
export class FfLoadingIndicatorComponent implements OnInit {
    progress$: Observable<boolean>;
    overlappingProgress$: Observable<boolean>;

    constructor(private store: Store<State>) { }
    ngOnInit(): void {
        this.progress$ = this.store.select(s => s.app.progress);
        this.overlappingProgress$ = this.store.select(s => s.app.overlappingProgress);
    }
}

ff-loading-indicator.component.html

<div [ngClass]="(progress$ | async) ? 'display-block' : 'display-none'">
    <mat-progress-bar class="ff-z-index-above-toolbar ff-general-loading-indicator" mode="indeterminate" color="accent"></mat-progress-bar>
</div>

最后在app.component.html.

app.component.html

<ff-loading-indicator></ff-loading-indicator>

您可以在stackblitz中找到更改。

注意:FfLoadingIndicatorComponent可以直接导入 AppModule. 由于我更喜欢​​模块方法,因此我创建了一个单独的模块并将其导入AppModule


推荐阅读