【问题标题】:Use a subscription inside a route resolver在路由解析器中使用订阅
【发布时间】:2019-10-23 16:43:02
【问题描述】:

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

服务应该返回的数据,并不是路由解析后组件要使用的数据,这是对resolver的明确使用,也解释了为什么resolver必须返回一个Observable。

我真正需要服务返回的数据,是在完全解决路由之前在几个可能的操作之间做出决定。

我为什么会陷入这种境地?

我的情况是,带有 slug 参数的顶部 url 可能需要呈现不同的组件。

示例

example.com/whatever-post-slug --> 需要加载 PostComponent
example.com/other-page-slug --> 需要加载 PageComponent
example.com/new-slug-for-random-post --> 需要加载 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 });
       )
}

由于我在解析器中订阅,与正在解析的路由关联的组件在navigation() 发生之前加载。

这会导致非常难看的导航效果。

问题是

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

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

如果您意识到,我将{ skipLocationChange: true } 放入router.navigate(),因为我希望用户在浏览器中保留路径。

我知道这似乎是我在滥用 Angular 的路由系统,但我想达到这个结果。

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

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

一个有效的想法

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

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

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

【问题讨论】:

    标签: angular rxjs angular2-routing


    【解决方案1】:

    对我来说,要求是等到数据被提取(如果不存在)。因此,我遵循了在解析器中使用 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 获取数据,因此在我的情况下订阅来自 store 的 observable 可以在可用时获取最新数据。希望这对有需要的人有所帮助。

    【讨论】:

      【解决方案2】:

      看起来您正试图在解析器内部进行重定向,而在 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;
           })
         );
      }
      

      【讨论】:

      • 感谢您的帮助。我在httpResponse =&gt; { 之后添加了console.log('Hello');,但它从不显示。你确定这是正确的吗?另外,这实际上在做什么?
      • mergeMap 切换到一个新的 Observable(在本例中为 EMPTY,因为您不希望执行实际路由)。如果它不打印任何内容,则意味着 this.wprestNoAuthSrv.getPermalinkStructure() 不会发出任何值,这表明在您的原始代码中它也不应该显示 hello 消息
      • 感谢您的解释。好吧,在这种情况下,原始代码有效。但是当我使用你的例子时,它没有。我的理解是,在我们订阅之前,observable 不会发出数据。使用mergeMap真的不需要订阅吗?
      • 它是被隐式订阅的——毕竟你返回 Observable。哦,没有返回。请尝试添加它 - return this.wprestNoAuthSrv.getPermalinkStructure().pipe(.
      • 不在mergeMap中,但是你可以添加catch(err =&gt; {})。它必须返回一个 Observable,因此如果您的目标只是重定向到某个路线,您可以再次 return EMPTY。前任。 catch(err =&gt; this.router.navigate(somewhere...); return EMPTY;)
      猜你喜欢
      • 2015-09-30
      • 1970-01-01
      • 2019-02-05
      • 2023-03-31
      • 2020-10-26
      • 2018-10-31
      • 1970-01-01
      • 1970-01-01
      • 2017-05-15
      相关资源
      最近更新 更多