首页 > 解决方案 > 如何在 Angular 中使用 useFactory 根据某些条件延迟加载模块?

问题描述

我有一个包含大量模块和组件的 Angular 项目,我想根据一些业务逻辑显示模块的组件。我想使用 Angular 的强大功能,即延迟加载,这将使我的应用程序精简。

我想做的事 :

假设,我有一个 bookStore 网站,很多读者在该网站上花时间阅读在线文章。我想展示基于reader's role. 如果读者的角色是管理员或员工,那么他们可以访问所有功能和组件。如果读者的角色是,normal那么他们只能查看某些组件。

为此,我制作了这种结构:

在此处输入图像描述

为实现此功能。我们必须保持模块的路径相同,为此,我必须使用 angular 的 useFactory 方法,这将允许我运行自定义函数进行更新InjectionToken(ROUTES),该函数已经被 angular 本身用于编译和加载模块。

我的模块流程是这样的:

在此处输入图像描述

所有三个模块(reader-handler、admin/reader 和 user/reader)都是延迟加载的模块。reader-handler 模块的路径在路由文件中定义,但其他两个模块的路径将使用 useFactory 函数动态附加在 ROUTES 中。

这是我的代码:

providers: [{
    provide: ROUTES,
    useFactory: decideWhichModuleToLoad,
    deps: [ReaderService],
    multi: true,
  }]

export function decideWhichModuleToLoad(readerService: ReaderService) {
   readerService.getReaderType().subscribe((result) => {
    let routes: Routes = [];
    if (result && result?.Role) {

      if (result.Role == 'admin') {
        routes = [{
          path: '',
          loadChildren: () =>
            import('../admin/read.module').then((m) => m.ReadModule)
        }]
      }
      else {
        routes = [{
          path: '',
          loadChildren: () => import('../user/read.module').then((m) => m.ReadModule)
        }]
      }
    }
    else {
      routes = [{
          path: '',
          loadChildren: () => import('../user/read.module').then((m) => m.ReadModule)
        }]
    }
    return routes;
  });
}

read-handler我在模块 So中编写了这段代码。当这个模块加载时,它将useFactory执行并更新ROUTESangular 已经使用的变量。

如果我直接返回路由而不等待服务的条件和订阅响应,它可以工作并加载模块。如果我运行上面的代码,那么动态加载的我的子组件和模块将是未定义的。ROUTES因为 Angular在我们更新它之前已经编译好了。错误如下所示:

在此处输入图像描述

我正在关注动态路由教程:如何在同一路由上加载不同的模块 并且 Angular 说在 useFactory 解析之前,Angular 编译ROUTESinjectionToken 是 Angular 的错误。(无法读取未定义的属性“loadChildren”错误

任何人都知道我该如何解决这个错误......并保持角度等待解决usefactory。任何小建议都会对我有所帮助。

标签: angulardynamicroutes

解决方案


经过大量研究和@OLO 的回答,我应用的整体解决方案是这样的:

  1. 制作了一个处理程序模块(ReaderHandlerModule - 决定应该加载什么的模块)
  2. 将此处理程序模块加载到路径上的常规路由数组上,例如:'/reader'
[ ..., {
  path: '/reader', 
  loadChildren: () =>
            import('../../../shared/reader-handler.module').then((m) => m.ReaderHandlerModule)
}, ... ]
  1. 制作了一项可以从 API 获取一些数据的服务,以检查阅读器的作用是什么

  2. 将服务的函数调用到服务的构造函数中,并将结果存储在服务的一个behaviorSubject变量中,以便它可以在任何地方订阅以获取数据。

ReaderHandlerModule.ts

import { NgModule } from "@angular/core";
import { Routes, ROUTES } from "@angular/router";
import { ReaderService} from "app/_services";
import { SharedModule } from "./shared.module";

export function configReaderRoutes(readerService: ReaderService) {
  let routes: Routes = [];
  if (readerService.readerRole) {
    if (readerService.readerRole == 'User') {
      routes = [{
        path: '',
        loadChildren: () =>
          import('../user/userReader.module').then(
            (m) => m.ReaderModule
          )
      }]
    } else if (readerService.readerRole == 'Admin') {
      routes = [{
        path: '',
        loadChildren: () =>
          import('../admin/adminReader.module').then(
            (m) => m.ReaderModule
          )
      }]
    }
    else {
      routes = [{
        path: '',
        loadChildren: () =>
          import('../user/userReader.module').then(
            (m) => m.ReaderModule
          )
      }]
    }
  }
  else {
    routes = [{
      path: '',
      loadChildren: () =>
          import('../user/userReader.module').then(
            (m) => m.ReaderModule
          )
    }]
  }
  return routes;
}

@NgModule({
  declarations: [],
  imports: [
    SharedModule
  ],
  providers: [{
    provide: ROUTES,
    useFactory: configReaderRoutes,
    deps: [ReaderService],
    multi: true,
  }]
})
export class ReaderHandlerModule { }

读者服务.ts

export class ReaderService implements OnDestroy {
    public readerRole: 'User' | 'Admin' = 'User';
    private readerTypeSubject: BehaviorSubject<any>;
    public readerTypeObservable: Observable<any>;

    constructor(public router: Router) {
        this.readerTypeSubject = new BehaviorSubject<any>('User');
        this.readerTypeObservable = this.readerTypeSubject .asObservable();

        this.getReaderType().subscribe(result => {
            this.readerRole = result.readerType
        })
    }

    getReaderType() {
       return this.readerTypeSubject.asObservable();
    }

    fetchReaderType() {
        .... Fetch data from API and store in readerTypeSubject.
        // So getReaderType().subscribe() will fire and readerRole variable from this service will update.
        // FetchReaderType is called before load this handler Module (/reader). 
    }

如果要重新加载处理程序模块以重新获取并根据再次条件检查重新加载模块。然后使用此功能:

refreshRoutes() {
        const i = this.router.config[1].children.findIndex(route => route.path === 'reader');
        this.router.config[1].children.splice(i, 1);
        this.router.config[1].children.push({
            path: 'reader',
            import('../../../shared/reader-handler.module').then((m) => m.ReaderHandlerModule),
        })
    }

推荐阅读