Warm tip: This article is reproduced from serverfault.com, please click

其他-NGXS如何在异步调用后触发Angular重新渲染?

(其他 - NGXS How to trigger Angular re-render after asynchronous call?)

发布于 2020-11-20 10:47:06

我正在从我的商店进行一些api调用,并且遇到一个catch错误,当引发错误时,该错误会触发带有错误消息的模式。问题是,发生这种情况时,将调用触发模式的方法,但是直到我单击页面上的某个位置时,HTML才会呈现。这仅在商店内部发生,我在应用程序的多个部分进行了模拟,如下所示:

timer(5000)
      .pipe(
        mergeMap(() => {
          throw new Error('Some error');
        }),
      )
      .pipe(
        catchError((error) => {
          return this.handleError(_(`Couldn't do the thing`))(error);
        }),
      )
      .subscribe((result) => {
        console.log(result);
      });

我以为我可以注入ChangeDetectorRef来触发html的手动重新渲染,但是我得到了NullInjectorError: No provider for ChangeDetectorRef!但我无法使其正常工作。我的问题是:

可以ChangeDetectorRef在商店中注入吗?它可以解决我的问题吗?另外,作为后续问题,还有其他方法可以规避此问题吗?根据我读过的一些东西,这似乎是由于商店不在Angular范围内导致的,所以它不知道需要重新渲染某些东西。

任何帮助将非常感激。

更新:是通过分派操作以显示错误消息的方式来说明问题和可能的解决方案的快速解决方案。

Questioner
António Quadrado
Viewed
11
kremerd 2020-11-28 07:51:07

通常,更改检测是由zone.js自动触发的,更具体而言,是在NgZone中注册的每个(微)任务之后进行。

默认情况下,NGXS在NgZone中不运行动作处理程序。这是有用的性能优化,在大多数情况下,你不会注意到它们之间的差异。当操作处理程序仅修改实际状态且没有副作用时,尤其如此。但是在你的情况下,动作处理程序会产生副作用:this.handleError(_("Couldn't do the thing"))(error),或confimationService.triggerConfirmation()在StackBlitz中。而且这种副作用甚至反映在视图中。

现在,仍然有很多方法可以解决这个问题。你所需要做的就是在副作用发生后触发一个更改检测周期这就是真正有趣的地方:尽管动作处理程序本身不在NgZone中运行,但是在NgZone中却有很多周围的代码在运行。这确实可能会触发上述变更检测周期。特别是:

  • 如果你的操作是同步的,并且调度它的代码在NgZone中运行,则在当前(微)任务结束时触发的更改检测周期将在副作用之后运行。
  • 如果你订阅从Observable返回的store.dispatch,则该订阅将在NgZone内部发出并完成。因此,这会在产生副作用后触发两个变化检测周期。

(顺便说一句,后者是为什么在你的Stackblitz中调度两个嵌套动作的原因:你在此处订阅了dispatch方法!)

如果你想自己调查事件的顺序,请查看此Stackblitz控制台输出应该可以非常准确地告诉你每种情况下的情况。

最后,让我们谈谈如何确保正确触发变更检测。实际上,有两个选项可供选择:

  1. 虽然你无法ChangeDetectorRef在状态下插入,但可以插入ApplicationRef调用其tick方法将在当前(微)任务结束时异步触发变更检测周期。
  2. 如果要利用zone.js,还可以注入NgZone并使用其run方法在NgZone中运行整个副作用。这样做还有一个好处,就是副作用检测到的(微)任务也将跟随变化检测周期。
  3. 如果要在NgZone中运行所有代码,则可以executionStrategy: NoopNgxsExecutionStrategy在中进行设置NgxsConfig这将覆盖NGXS的默认行为,并全局导致所有操作处理程序在NgZone中运行。