在路由解析器中使用订阅

Use a subscription inside a route resolver

我想向服务请求一些数据,以允许路由解析器执行特定操作。

应该由服务 returned 的数据,不是路由解析后组件将使用的数据,这是解析器的明确使用,这解释了为什么解析器必须 return 一个 Observable。

我真正需要由服务 return 编辑的数据是为了在路由完全解析之前在几个可能的操作之间做出决定。

为什么我会落到这种地步?

我遇到的情况是带有 slug 参数的顶部 url 可能需要渲染不同的组件。

示例

example.com/whatever-post-slug --> needs to load PostComponent
example.com/other-page-slug --> needs to load PageComponent
example.com/new-slug-for-random-post --> needs to load PostComponent
...

为什么我不直接给路由加前缀?

因为我不能。我正在为 WordPress 创建一个主题,并允许类似的 url 用于帖子和页面。详情请参阅 this question

我的解析器实际上在做什么?

为了便于理解,我将只展示部分代码:

resolve(route: ActivatedRouteSnapshot): Observable<any>|any{
   this.wprestNoAuthSrv.getPermalinkStructure()
       .pipe(takeUntil(this.unsubscribeOnDestroy))
       .subscribe(
       // ... check httpResponse and decide where to go
          this.router.navigate(route, { skipLocationChange: true });
       )
}

因为我在解析器内部订阅,与正在解析的路由关联的组件会在 navigate() 发生之前加载。

这导致导航效果非常丑陋。

问题是:

如何在仍然使用该订阅的情况下阻止这种情况发生?

我实际上希望该组件永远不会加载,因为总会有 navigate() 进行。

如果你知道,我把{ skipLocationChange: true }放到了router.navigate(),因为我希望用户在浏览器中保留路径。

我知道这看起来像是我在滥用 Angular 的路由系统,但我想达到那个结果。

这个问题不是关于我想动态加载不同组件的事实(为此我发布了上面的链接问题)

这个问题是关于所选择的方法,它是有效的,但有一个我想避免的缺点。

一个可行的想法:

这个想法可行,但也许您知道更好的解决方案。如果我在 resolve() 方法中创建一个 observbe 并 return 它,我可以强制解析器等待,并且重定向将在它完成时完成。

return Observable.create(observer => {
  setTimeout(() => {
    observer.next("Finished");
    observer.complete();
  }, 5000);
});

当然,这是一个弥天大谎,不太好听。即使它工作得很好,我也需要知道这是否是一件非常糟糕的事情,或者是否有更好的选择。

看起来您正试图在解析器内部重定向并在 Observable 内部订阅从来都不是一个好主意。这应该符合您的期望:

resolve(route: ActivatedRouteSnapshot): Observable<any>|any{
   return this.wprestNoAuthSrv.getPermalinkStructure().pipe(
     takeUntil(this.unsubscribeOnDestroy),
     mergeMap(httpResponse => {
       // ... check httpResponse and decide where to go
       this.router.navigate(route, { skipLocationChange: true });
       return EMPTY;
     })
   );
}

对我来说,要求是等到数据不存在时才获取。因此,我遵循了在解析器中使用 promise 的方法,如下面的示例代码所示。

   public resolve(
        route: ActivatedRouteSnapshot,
        state: RouterStateSnapshot) {

    return new Promise((resolve, reject) => {
       this.store.getDataObservable().subscribe((data)=> {
            if(data){
              resolve(data)
            } else {
              this.store.dispatch(fetchData());
            }
         });                
    });
}

我有 NgRx 实现来通过操作从 API 获取数据,因此在我的情况下订阅来自商店的可观察对象可以随时获取最新数据。希望这对有需要的人有所帮助。