0

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

意味着

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

我为什么要这样做?

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

答案是:我正在创建(顺便说一句,开源的)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 功能,它允许以直截了当的方式做到这一点。

4

1 回答 1

3

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

您可以使用一个模块来处理应该加载的组件,方法是使用 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 路由器已经知道他必须加载哪个组件。

总之,在 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

于 2019-10-22T02:04:42.707 回答