Angular: 如何根据服务在同一路径上路由不同的模块

Angular: How to route different modules on the same path depending on service

假设一个服务 ABService 有一个方法 isAResponsible: () => boolean 和两个模块:AModuleBModule

问题是:是否可以根据isAResponsiblereturns在AModuleBModule之间切换?如果 isAResponsible 的值发生变化,我们如何 'reroute' 并重新渲染? ABService 可能对其他服务有多种依赖性,因此最好以某种方式使用 DI 系统。

示例: 如果感兴趣的路线是 /aorbABService.isAResponsible returns true,那么我们想要路线 AModule。如果 ABService.isAResponsible returns false 但是我们希望 BModule 管理进一步的路由。请注意,一切都应该发生在 shared 路线上。

我用守卫和canActivate/canLoad试过了,但没有成功:

app.module.ts

import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { AorBActivateGuard } from './aorb-activate.guard';
import { ABService } from './ab.service';

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot([
      {
        path: 'aorb',
        canActivate: [AorBActivateGuard],
        loadChildren: () => import('./a/a.module').then((m) => m.AModule),
      },
      {
        path: 'aorb',
        loadChildren: () => import('./b/b.module').then((m) => m.BModule),
      },
    ]),
  ],
  providers: [AorBActivateGuard, ABService],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
})
export class AppModule {}

ab.service.ts

import { Injectable } from '@angular/core';

@Injectable()
export class ABService {
  private aIsResponsible = true;

  constructor() {}

  switch() {
    this.aIsResponsible = !this.aIsResponsible;
  }

  isAResponsible() {
    return this.aIsResponsible;
  }
}

aorb-activate.guard.ts

import { Injectable } from '@angular/core';
import {
  CanActivate,
  ActivatedRouteSnapshot,
  RouterStateSnapshot,
} from '@angular/router';
import { Observable } from 'rxjs';
import { ABService } from './ab.service';

@Injectable()
export class AorBActivateGuard implements CanActivate {
  constructor(private abService: ABService) {}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | Promise<boolean> | boolean {
    return this.abService.isAResponsible();
  }
}

你可以试试这个吗,我刚刚在本地检查过它是否有效,只要换一种方式思考,你就有了解决方案:)

import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { AorBActivateGuard } from './aorb-activate.guard';
import { ABService } from './ab.service';

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot([
      {
        path: 'aorb',
        loadChildren: () => {
          if ((new ABService()).isAResponsible()){
            return import('./a/a.module').then((m) => m.AModule)
          } else {
            return import('./b/b.module').then((m) => m.BModule)
          }
        },
      },
    ]),
  ],
  providers: [AorBActivateGuard, ABService],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
})
export class AppModule {}

可以在 AppModule 中注入 ABService 并监听来自 ABService 的变化。每次发生变化时,我们都可以重新配置(= 切换模块)路由器并让它导航相同的路由。这样我们总是加载负责的模块。

[...]
@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot([], {
      initialNavigation: 'disabled',
    }),
  ],
  providers: [ABService, BooleanStorageService],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
})
export class AppModule {
  constructor(private router: Router, private abService: ABService) {
    abService.onABChange.subscribe(this.resetRouterConfig(true).bind(this));
    this.resetRouterConfig(false)();
    this.router.initialNavigation();
  }

  reloadCurrentRoute() {
    const currentUrl = this.router.url;
    this.router.navigateByUrl('/', { skipLocationChange: true }).then(() => {
      this.router.navigate([currentUrl]);
    });
  }

  resetRouterConfig(refresh: boolean) {
    return () => {
      const constructedConfig = [
        {
          path: '',
          pathMatch: 'full',
          redirectTo: 'aorb',
        },
        {
          path: 'aorb',
          loadChildren: () => {
            if (this.abService.isAResponsible()) {
              return import('./a/a.module').then((m) => m.AModule);
            } else {
              return import('./b/b.module').then((m) => m.BModule);
            }
          },
        },
      ];

      this.router.resetConfig(constructedConfig);

      if (refresh) this.reloadCurrentRoute();
    };
  }
}

您可以找到一个工作示例 here