【问题标题】:Create a Routes path that loads a specific component based on condition创建一个基于条件加载特定组件的 Routes 路径
【发布时间】:2019-10-25 17:35:19
【问题描述】:

我想为相似的结构化路径加载不同的组件。

意思

example.com/whatever-post-slug --> 加载帖子组件
example.com/whatever-page-slug --> 加载页面组件
example.com/hello-i-am-a-string --> 加载帖子组件(因为 slug 属于一个帖子)
example.com/about-us --> 加载页面组件(因为 slug 属于一个页面)

我为什么要这样做?

可能有人会想:嘿嘿,但是你为什么不直接定义两个前缀不同的路径,路径是干什么用的呢?

答案是:我正在创建(顺便说一下open source)Angular WordPress 主题,我希望路由是用户决定的,而不是强制任何硬编码结构。

在多种可能性之一中,帖子和页面(如果您不了解 WordPress,只需知道这是两种不同的内容类型,我想使用独立的模块和组件)可能会在根级别共享 slug。

乍一看,我不知道 slug 是属于帖子还是页面或其他东西(所以UrlMatcher 在这里无法提供帮助)。

我有没有想到解决办法?

是的,三个。

第一个解决方案

我可以创建一个包装器组件,它将为包罗万象的路线加载,然后,在这个组件内部执行以下操作:

<whatever-a-component *ngIf="showComponentA()"><whatever-a-component>
<whatever-b-component *ngIf="showComponentB()"></whatever-b-component>

并让 Wrapper 组件完成所有逻辑。

这会在游戏中添加一个额外的中间组件。

第二种解决方案

为包罗万象使用解析器,然后在解析器中执行所有逻辑。

问题是,我需要订阅一个 http 通信才能知道我正在处理什么样的内容类型,并且由于 Resolver resolve() 方法需要返回一个 Observable,所以在那里订阅并不好。

当然,如果我返回一个等待一段时间的占位符 observable,它就可以工作,就像这样:

// ... inside resolve()
// Force an observable return to allow suscriptions take their time
return Observable.create(observer => {
  setTimeout(() => {
    console.log("Resolver observable is done");
    observer.complete();
  }, 15000);
});

...或者如果我使用mergeMap() 管道订阅并在我从订阅中获得结果时返回 EMPTY。

一旦我取回我的数据,我就可以设置新的路线,包括必须指向其特定组件的当前路径。

这对我来说似乎不是一个非常干净的方法。

第三种解决方案

只需加载一个普通的 Dispatcher 组件,它将在 OnInit() 进行所有检查,然后导航到一个“秘密”组件特定的 url,但使用 { skipLocationChange: true },因此用户将拥有正确的路由和正确的组件将被加载。

不过,这又在游戏中添加了一个额外的中间组件。

我认为这是最干净的解决方案,因为在 App Routing 模块中我可以执行以下操作:

{
path: 'wp-angular/view-post/:id',
loadChildren: () => import('./view-post/view-post.module').then(m => m.ViewPostModule)
},
{
path: 'wp-angular/view-page/:id',
loadChildren: () => import('./view-page/view-page.module').then(m => m.ViewPageModule)
}

所以,只有当用户实际访问这两种内容类型之一时,我才会延迟加载这些模块。

此外,如果用户随后访问相同类型的第二个内容,该内容类型组件将已经可用。

而且我可以使用{ skipLocationChange: true }这一事实将使路径保持预期成为可能。

另外,这允许显示导航加载反馈,而无需订阅路由器事件。

问题

你会做什么,为什么?

也许我错过了一些神奇的 Angular 功能,它允许以直截了当的方式做到这一点。

【问题讨论】:

    标签: angular angular-resolver


    【解决方案1】:

    一旦我遇到了类似的问题,我就这样解决了:

    您可以使用一个模块来处理应该加载的组件,方法是使用 Angular 的 useFactory 提供程序提供 RouterModule 的 ROUTES。

    代码可能是这样的:

    // 处理程序模块

    @NgModule({
      declarations: [],
      imports: [
        CommonModule,
        RouterModule
      ],
      providers: [
        {
          provide: ROUTES,
          useFactory: configHandlerRoutes,
          deps: [CustomService],
          multi: true
        }
      ]
    })
    
    export class HandlerModule {}
    
    export function configHandlerRoutes(customService: CustomService) {
      let routes: Routes = [];
      if (customService.whatever()) {
        routes = [
          {
            path: '', component: AComp
          }
        ];
      } else {
        routes = [
          {
            path: '', component: BComp
          }
        ];
      }
      return routes;
    }
    

    那么在您的 AppRoutingModule 中,路径 '' 的模块将成为 HandlerModule:

    // AppRoutingModule
    
     {
        path: '',
        loadChildren: () => import('app/handler/handler.module').then(mod => mod.HandlerModule)
    }
    

    在 CustomService 之后,当提供方法 .whatever() 的值发生更改时,您必须更新 Router.config,因为应用程序只会加载第一次加载的组件。这是因为 HandlerModule 中的 useFactory 提供程序使用的函数“configHandlerRoutes”仅在我们第一次导航到“”路径时执行,之后,Angular Router 已经知道他必须加载哪个组件。

    在CustomService中你必须做的总结:

      export class CustomService {
      private whateverValue: boolean;
      constructor(private router: Router) {
      }
    
      public whatever(): boolean {
        return this.whateverValue;
      }
    
      public setWhatever(value: boolean): void {
        const previous = this.whateverValue;
        this.whateverValue = value;
        if (previous === this.whateverValue) {
          return;
        }
        const i = this.router.config.findIndex(x => x.path === '');
        this.router.config.splice(i, 1);
        this.router.config.push(
          {path: '', loadChildren: () => import('app/handler/handler.module').then(mod => mod.HandlerModule)}
        );
      }
    }
    

    就是这样。我在示例中使用了“”路径,但您可以使用任何您想要的路径。

    此外,如果您想加载模块而不是组件,您可以使用相同的方法。

    如果您想要其他参考,请参阅他们使用相同方法的文章:https://medium.com/@german.quinteros/angular-use-the-same-route-path-for-different-modules-or-components-11db75cac455

    【讨论】:

    • 我需要知道用户试图访问的路径。但我不能在 configHandlerRoutes() 中使用 ActivatedRouteSnapshot
    • 你可以添加ActivatedRoute作为提供者的依赖:@NgModule({ declarations: [], imports: [ CommonModule, RouterModule ], providers: [ { provide: ROUTES, useFactory: configHandlerRoutes, deps: [CustomService, ActivatedRoute], multi: true } ] }) 之后你可以在configHandlerRoutes()中使用它:export function configHandlerRoutes(customService: CustomService, route: ActivatedRoute) { ... }
    猜你喜欢
    • 1970-01-01
    • 2020-08-13
    • 1970-01-01
    • 1970-01-01
    • 2020-04-30
    • 1970-01-01
    • 1970-01-01
    • 2020-02-01
    • 1970-01-01
    相关资源
    最近更新 更多