Angular4 - 如何确保ngOnDestroy在导航之前完成

时间:2017-10-30 09:10:39

标签: javascript angular rxjs settimeout

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

我遇到的问题是,当用户返回列表组件时,子组件必须在ngOnDestroy方法中进行一些清理 - 这需要调用服务器来执行最终的补丁&# 39;对象。有时这种处理可能有点慢。

当然会发生什么是用户返回列表,并且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才能做到这一点)。 / p>

我想到的一个想法是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移动到&#34; YES&#34;对话框的方法处理程序,它仍然必须调用api,所以YES处理程序仍然会在API之前完成,并且我回来时遇到同样的问题。

更新

在阅读完所有评论后,我猜测解决方案是这样的。改变后卫:

    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...
      });

2 个答案:

答案 0 :(得分:3)

正如你所说,有很多方法,他们依赖于其他细节如何设置你的整个应用程序,数据流和ux-flow,但感觉你可能想看看CanDeactivate保护方法确保用户在Observable<boolean>|Promise<boolean>被解析为true之前无法离开路线。

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

<强> [UPDATE]

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

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)
  }

答案 1 :(得分:1)

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