ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后已更改-观察值更改后如何更新模板

时间:2019-06-13 13:59:41

标签: angular typescript rxjs angular-changedetection

我浏览了很多SO帖子,试图找到一个解决方案,我发现的唯一一个是hack实现。我也从我订阅的ngrx商店中获取了一个观察值:

this.navigationSelected$ = this.store.pipe(select(currentlySelectedNavigation));

this.navigationSelected$.subscribe(res => {
  ...
});

带有ngIf取决于模板内部的此可观察值:

<profile-navigation *ngIf="(navigationSelected$ | async) == navigationLayout[0].location"></profile-navigation>

每当NavigationSelected $的值更改时,都会引发:

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ngIf: [object Object]'. Current value: 'ngIf: false'.

,模板不会更新。我设法通过运行cdRef.detectChanges()解决了这个问题。在订阅结束时。它工作正常,但仍在引发错误,而且如上所述,它看起来像是黑客。

实现我想做的最好的方法是什么?

2 个答案:

答案 0 :(得分:2)

  

每当NavigationSelected $的值更改时,都会引发:

该错误表示该值已更改两次。

当选择器上出现这些错误时,很难修复它们。视图确实没有任何错误。问题在于商店在呈现视图之前和之后都在改变状态,这很可能意味着在调用setTimeout()之后应该进行调度。

问题在于,这会使源代码中的其他位置依赖于更改状态以保护视图免于触发错误。那不是理想的。

一种替代方法是使用EventEmitter发出值。

<profile-navigation *ngIf="(navigationSelectedSafe$ | async) == navigationLayout[0].location"></profile-navigation>

public navigationSelectedSafe$ = new EventEmitter<any>(true); // must be true

this.navigationSelected$.subscribe(res => navigationSelectedSafe$.emit(res));

使用EventEmitter(true)时,它将在setTimeout()之后发出值,以保护视图免受更改错误的影响。

您还可以在源代码中搜索以查找您使用@Output()的地方,并查看将其更改为EventEmitter(true)是否可以解决问题。

通常在选择器上看到此错误时。这意味着您正在视图之外执行许多与状态相关的工作。需要广播某些更改的组件应该使用@Output(),但是如果该组件正在分派,则它会绕过查看过程。这是您遇到这些问题的地方。

答案 1 :(得分:0)

我通常添加一个debounceTime(0)来解决此问题。

this.navigationSelected $ = this.store.pipe(select(currentlySelectedNavigation),debounceTime());