简体   繁体   English

如何在 angular 中使用 useFactory 根据某些条件延迟加载模块?

[英]How to lazy load module based on some condition using useFactory in angular?

I have an angular project with large amount of modules and components in which, I want to show module's component based on some business logic.我有一个包含大量模块和组件的 angular 项目,我想根据一些业务逻辑显示模块的组件。 I want using angular's powerful feature ie lazy loading which will allow me to make my application lite.我想使用 angular 的强大功能,即延迟加载,这将使我的应用程序精简。

What I want to Do:我想做的事:

Suppose, I have one bookStore site on which lot's of readers spent time with reading online articles.假设,我有一个 bookStore 网站,很多读者都花时间阅读在线文章。 I want to show different module based on reader's role .我想根据reader's role展示不同的模块。 if reader's role is admin or staff then they can access all the feature and components.如果读者的角色是管理员或员工,那么他们可以访问所有功能和组件。 if reader's role is normal then they can view only some component.如果读者的角色是normal的,那么他们只能查看某些组件。

For that, I make this kind of structure:为此,我制作了这种结构:

在此处输入图像描述

For achive this feature.为了实现这个功能。 we must keep our route same for both the module and for that, I have to use useFactory method of angular that will allow me to run custom function for update InjectionToken(ROUTES) which is already used by angular itself for compile and load module.我们必须为模块保持相同的路由,为此,我必须使用 angular 的 useFactory 方法,这将允许我运行自定义 function 以更新InjectionToken(ROUTES) ,angular 本身已将其用于编译和加载模块。

My module flow is like this:我的模块流程是这样的:

在此处输入图像描述

All three modules (reader-handler, admin/reader and user/reader) are lazy loaded modules.所有三个模块(reader-handler、admin/reader 和 user/reader)都是惰性加载模块。 reader-handler module's path is defined in route file but other two module's path will append in ROUTES dynamically using useFactory function. reader-handler 模块的路径在路由文件中定义,但其他两个模块的路径将在 ROUTES 中动态使用 useFactory function append。

Here is my code:这是我的代码:

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;
  });
}

I wrote this code in read-handler module So.我在read-handler模块 So 中编写了这段代码。 when this module load, it's useFactory will execute and update ROUTES variable which is already used by angular.当此模块加载时,它的useFactory将执行并更新已被 angular 使用的ROUTES变量。

It works and load module if I return route directly without wait for condition and subscription response from service.如果我直接返回路由而不等待服务的条件和订阅响应,它会工作并加载模块。 If I run above code then My child component and module that is load dynamically will be undefined.如果我运行上面的代码,那么动态加载的我的子组件和模块将是未定义的。 because angular already compile ROUTES before we update it.因为 angular 在我们更新之前已经编译了ROUTES Error is look like this:错误是这样的:

在此处输入图像描述

I am following this Tutorial for dynamic Routing: How to load different module on same route And Angular say that it's angular's bug that before useFactory resolve, angular compile ROUTES injectionToken.我正在关注这个动态路由教程: 如何在同一路由上加载不同的模块并且 Angular 说这是在 useFactory 解决之前的角度错误,angular 编译ROUTES injectionToken。 ( Cannot read property 'loadChildren' of undefined" error ) 无法读取未定义的属性“loadChildren”错误

Anyone guys know how can I resolve this error... and keep angular waiting for resolve usefactory .任何人都知道我该如何解决这个错误......并让 angular 等待 resolve usefactory Any small suggestion would be helpful for me.任何小建议都会对我有所帮助。

After Lot's of research and @OLO's answer, Total Solution that I applied is like this:经过大量研究和@OLO 的回答,我应用的 Total Solution 是这样的:

  1. Made one handler module (ReaderHandlerModule - module that decide what should load)制作了一个处理程序模块(ReaderHandlerModule - 决定应该加载什么的模块)
  2. Load this handler module on regular route array on path like: '/reader'将此处理程序模块加载到路径上的常规路由数组中,例如:'/reader'
[ ..., {
  path: '/reader', 
  loadChildren: () =>
            import('../../../shared/reader-handler.module').then((m) => m.ReaderHandlerModule)
}, ... ]
  1. Made one service that can get some data from API for check that what is the role of reader制作了一项服务,可以从 API 获取一些数据,以检查读者的角色是什么

  2. Call service's function into service's constructor and store result in service's one behaviorSubject variable so that it can subscribe anywhere to get data.将服务的 function 调用到服务的构造函数中,并将结果存储在服务的一个 behaviorSubject 变量中,以便它可以在任何地方订阅以获取数据。

ReaderHandlerModule.ts 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 { }

ReaderService.ts读者服务.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). 
    }

If you want to reload handler Module to refetch and reload module based on again condition check.如果要重新加载处理程序模块以再次根据条件检查重新获取和重新加载模块。 then use this function:然后使用这个 function:

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),
        })
    }

You're setting routes inside a subscribe and it's asynchronous.您在订阅中设置路由并且它是异步的。 You can try to call getReaderType before entering in decideWhichModuleToLoad function and store it somewhere so you can use it directly from your useFactory .您可以尝试在输入之前调用getReaderType decideWhichModuleToLoad function 并将其存储在某处,以便您可以直接从您的useFactory使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM