【问题标题】:Angular4 - how to ensure ngOnDestroy finishes before navigating awayAngular4 - 如何在导航离开之前确保 ngOnDestroy 完成
【发布时间】:2018-04-11 05:01:50
【问题描述】:

我有一个对象列表。用户可以点击一个,然后加载一个子组件来编辑该组件。

我遇到的问题是,当用户返回列表组件时,子组件必须在 ngOnDestroy 方法中进行一些清理 - 这需要调用服务器以对对象进行最终“修补” .有时此处理可能会有点慢。

当然,发生的情况是用户回到列表中,并且该 api 调用在来自 ngOnDestroy 的数据库事务完成之前完成,因此用户会看到陈旧的数据。

  ngOnDestroy(){
    this.destroy$.next();
    this.template.template_items.forEach((item, index) => {
      // mark uncompleted items for deletion
      if (!item.is_completed) {
        this.template.template_items[index]['_destroy'] = true;
      };
    });
    // NOTE 
    // We don't care about result, this is a 'silent' save to remove empty items,
    // but also to ensure the final sorted order is saved to the server
    this._templateService.patchTemplate(this.template).subscribe();
    this._templateService.selectedTemplate = null;
  } 

我知道不建议进行同步调用,因为它会阻塞 UI/整个浏览器,这不是很好。

我确信有多种方法可以解决这个问题,但真的不知道哪种方法最好(特别是因为 Angular 不支持同步请求,所以我不得不回退到标准 ajax 来做到这一点)。

我确实想到的一个想法是 ngOnDestroy 可以将“标记”传递给 API,然后它可以将该对象标记为“正在处理”。当列表组件进行调用时,它可以检查每个对象以查看它是否具有该标记,并为处于该状态的任何对象显示一个“刷新陈旧数据”按钮(无论如何,99% 的时间只会是单个项目,用户最近编辑的)。与仅将异步调用更改为同步调用相比,这似乎是一种废话解决方法,并且需要大量额外的代码。

其他人一定也遇到过类似的问题,但我似乎找不到任何明确的例子,除了this sync one

编辑

请注意,这个子组件上已经有一个 CanDeactive 保护。它要求用户确认(即放弃更改)。因此,如果他们单击确认,则执行 ngOnDestroy 中的此清理代码。但请注意,这不是用户真正“放弃”更改的典型角度形式。基本上在离开此页面之前,服务器必须对最终的数据集进行一些处理。所以理想情况下,我不希望用户在 ngOnDestroy 完成之前离开 - 我如何强制它等到该 api 调用完成?

我的 CanDeactive 守卫的实现与 Hero 应用程序的 official docs 中的实现几乎相同,挂钩到一个通用对话服务,提示用户他们是希望留在页面上还是继续离开。这里是:

  canDeactivate(): Observable<boolean> | boolean {
    console.log('deactivating');
    if (this.template.template_items.filter((obj) => { return !obj.is_completed}).length < 2)
      return true;

    // Otherwise ask the user with the dialog service and return its
    // observable which resolves to true or false when the user decides
    return this._dialogService.confirm('You have some empty items. Is it OK if I delete them?');
  }

文档并没有说明我的情况 - 即使我将清理代码从 ngOnDestroy 移动到对话框的“YES”方法处理程序,它仍然必须调用 api,因此 YES 处理程序仍然会完成在 API 之前,我又遇到了同样的问题。

更新

在阅读了所有 cmets 之后,我猜测解决方案是这样的。换防:

    return this._dialogService.confirm('You have some empty items. 
        Is it OK if I delete them?');

    return this._dialogService.confirm('You have some empty items.
        Is it OK if I delete them?').subscribe(result => {
      ...if yes then call my api and return true...
      ...if no return false...
      });

【问题讨论】:

  • 查看 CanDeactivate 或一般的守卫,例如查看here
  • 谢谢,这不是解决方案。我将更新问题以解释原因。
  • @rmcshry 这是一个解决方案。您在CanDeactivate 中的用户确认不是解决方案。
  • 如果这是解决方案,那为什么它不起作用?我已经有一个 CanDeactivate 守卫。当警卫激活时,会提示用户。当它们“继续”离开时,调用 ngOnDestroy 来完成清理工作。

标签: javascript angular rxjs settimeout


【解决方案1】:

正如你所说,有很多方法,它们取决于其他细节如何设置你的整个应用程序、数据流和 ux 流,但感觉你可能想看看 CanDeactivate 保护方法,它可以确保在您的Observable&lt;boolean&gt;|Promise&lt;boolean&gt; 解析为true 之前,用户不能离开路线。

因此,这是一种异步等待,直到您的服务确认服务器上的内容发生更改的方式。

[更新]

这取决于您的用户确认实施,但这些方面...

waitForServiceToConfirmWhatever(): Observable<boolean> {
    return yourService.call(); //this should return Observable<boolean> with true emitted when your server work is done
  }

canDeactivate(): Observable<boolean> {

    if(confirm('do you want to leave?') == true)   
      return this.waitForServiceToConfirmWhatever();
    else
      Observable.of(false)
  }

【讨论】:

  • 谢谢,但是路线已经有一个 CanDeactive 守卫。它提示用户是/否,如果他们回答是,则触发执行清理的 ngOnDestroy 代码。在那之前我不能做清理工作。因此是第 22 条规则。
  • 我要做的是让是/否提示就像组件中的常规点击处理程序一样。然后如果用户点击是,我会router.navigate['whereverYourListIs']。然后,按照我的回答中所述使用CanDeactivate。我认为您没有理由在 CanDeactivate 守卫中处理是/否。这属于组件本身,甚至在组件ngOnDestroy 中也不属于它,因为它只是一个常规的用户操作,就像其他任何与组件生命周期无关的事情一样,因为用户的简单“不”应该什么都不做。希望这是有道理的。
  • 我不处理是/否。用户可以导航到任何地方,我不知道(或关心)他们下一步可能想去哪里。 CanDeactive 会在他们选择去的任何地方开火。它检测是否还有工作要做(即对象未处于“最终”状态)并且守卫抛出提示(基本上,您确定要离开并扔掉其他东西吗?)。单击 NO 没有任何作用,因为 CanDeactive 的本意是 - 用户停留在页面上。如果他们点击是,守卫允许他们继续,组件被卸载并自动调用 ngOnDestroy。
  • 我认为答案可能在于您的陈述“用户不能离开路线,直到您的 Observable|Promise 被解析为真” - 那么我如何改变我的警惕以确保通话离开前解决API?例如,我可以将 api 调用链接到对话服务调用吗?
  • 好的,是什么阻止您在用户单击“是”后向您的服务发出额外请求并等待其Observable&lt;boolean&gt; 解析为true,然后允许用户离开(@987654331 @return true)?
【解决方案2】:

我能想到的一个“解决方法”是让您的列表基于客户端。您将列表作为 JS 数组或对象,并基于此显示 UI。在详细信息屏幕中进行编辑后,在ngOnDestroy 上调用的服务在更新其他相关数据时清除该项目上的陈旧标志。

【讨论】:

  • 这是一个我真正喜欢的好主意,并且可能在 v2 中必须这样做以支持“离线”模式(加上使用本地存储)。所以 +1,谢谢你的想法。
猜你喜欢
  • 2013-11-21
  • 2019-12-16
  • 2018-11-17
  • 2016-09-12
  • 2014-12-01
  • 2012-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多