【问题标题】:Lazy load module on same path based on guard基于守卫的同一路径上的延迟加载模块
【发布时间】:2019-09-03 06:42:59
【问题描述】:

我想基于同一 ("") 路径 (home) 上的用户角色加载特定的 Angular 模块。假设我有两个模块,名为 AdminModule 和 OperatorModule。如果角色是 ADMIN,那么我想加载 AdminModule,否则加载 OperatorModule。我想用 Angular Guard 存档。

现在在app.routing.ts我添加了以下代码:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { AuthGuard } from './core/guards/auth.guard';

import { AdminModule } from './modules/admin';
import { OperatorModule } from './modules/operator';

const routes: Routes = [
   {
      path: '',
      loadChildren: () => AdminModule,
      canLoad: [ AuthGuard ],
      data: { role: 'ADMIN' }
   },

   {
      path: '',
      loadChildren: () => OperatorModule,
      canLoad: [ AuthGuard ],
      data: { role: 'OPERATOR' }
   },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

我已经使用以下代码实现了一个 AngularGuard,它必须显示 OperatorModule:

import { Injectable } from '@angular/core';
import { CanLoad, Route, Router } from '@angular/router';

@Injectable({
   providedIn: 'root'
})
export class AuthGuard implements CanLoad {
   constructor(private router: Router) {}

   canLoad(route: Route): Promise<boolean> | boolean {
      if (route.data.role === 'OPERATOR') {
         return true;
      }

      return false;
   }
}

在第一条路线失败后它以某种方式停止寻找,我做错了吗?

djerid 的 StackBlitz 示例:https://stackblitz.com/edit/angular-vd4oyu?file=app%2Fapp-routing.module.ts

===

Matcher 也不起作用:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { AuthGuard } from './core/guards/auth.guard';

import { AdminModule } from './modules/admin';
import { OperatorModule } from './modules/operator';

const routes: Routes = [
   {
      loadChildren: () => AdminModule,
      matcher: AdminMatcher,
      data: { role: 'NO' }
   },

   {
      loadChildren: () => OperatorModule,
      matcher: OperatorMatcher,
      data: { role: 'OPERATOR' }
   },
];

@NgModule({
   imports: [RouterModule.forRoot(routes)],
   exports: [RouterModule]
})
export class AppRoutingModule {
   constructor() {

   }
}


import { UrlSegment, UrlSegmentGroup, Route } from '@angular/router';
export function AdminMatcher(segments: UrlSegment[], group: UrlSegmentGroup, route: Route) {
   const isPathMatch = segments[0].path === route.path;

   if (isPathMatch && route.data.role === 'ADMIN') {
      return { consumed: [segments[0]] };
   } else {
      return null;
   }
}

export function OperatorMatcher(segments: UrlSegment[], group: UrlSegmentGroup, route: Route) {
   const isPathMatch = segments[0].path === route.path;

   if (isPathMatch && route.data.role === 'OPERATOR') {
      return { consumed: [segments[0]] };
   } else {
      return null;
   }
}

【问题讨论】:

  • 你能在stackblitz上重现这个问题吗?
  • 这里有一个类似的问题:stackoverflow.com/questions/49405281/…
  • @MichaelDesigaud 我明白了,但他们的解决方案是将模块放在不同的路径上。我认为这是不可能的,对吧? :(
  • @TilakDewangan 添加了与 djerid 完全相同的问题的 Stackblitz :)
  • @YanickvanBarneveld 是的,如果您有相同的路径,最后一个将覆盖其他路径

标签: angular lazy-loading angular-module angular-guards


【解决方案1】:

最好是使用 UrlMatchers 而不是 canLoad ,根据您的条件匹配每一个,当 2 个路径之一匹配时,另一个将被自动忽略

 const routes: Routes = [{
  path: '',
  matcher: adminMatcher,
  oadChildren: () => AdminModule,
  data: { role: 'ADMIN' }
 },
 {
  path: '',
  matcher: operatormatcher,
  loadChildren: () => OperatorModule,
  data: { role: 'OPERATOR' }

 }]

检查这个example

【讨论】:

  • 我试过但似乎不起作用,请参阅编辑后的答案。删除 isPathMatch 也不起作用... :(
【解决方案2】:

这是一个老问题,但我在这里留下一个答案,以防有人偶然发现这个问题。我通过在我的延迟加载路由中使用根模块的injector 解决了这个问题。

您可以在stackblitz 上查看有效的解决方案。

解决方案

步骤 1

创建一个导出 ReplaySubject&lt;Injector&gt; 的新文件。

import { Injector } from '@angular/core';
import { ReplaySubject } from 'rxjs';

// Feel free to optimize this implementation if you want to
// expose `Observable<Injector>` instead of the subject itself.
export const appInjector = new ReplaySubject<Injector>();

第二步

main.ts 中,获取根模块的Injector 的句柄并将其发布到您在上面创建的appInjector 主题:

platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .then((m) => appInjector.next(m.injector)) // publish root module's injector
  .catch((err) => console.error(err));

第三步

这是我们根据一些异步条件修改需要异步延迟加载不同模块的路由的步骤。

const routes: Routes = [
  // other routes
  // ...
  // ...
  // conditional route
  {
    path: "dashboard",
    component: LayoutComponent,
    canActivate: [DashboardAuthGuard], // block this route if user is not logged-in
    loadChildren: () =>
    
      // Use the appInjector subject
      appInjector.pipe(
        
        // ...to get a handle to your AuthService
        map((injector) => injector.get(AuthService)),
        
        // ...then switch to a new observable
        switchMap((authService) => {
          
          // ...that uses authService to retrieve the logged-in user
          return authService.user$.pipe(
            
            // ...then switches again, this time to actually lazy-load a feature module
            switchMap((user) => {
              
              // ...but first let's check the user's role
              switch (user.role) {
                
                // ...and load Admin Feature Module if user.role is 'admin'
                case "admin":
                  return import(
                    "./modules/admin-dashboard/admin-dashboard.module"
                  ).then((m) => m.AdminDashboardModule);
                
                // ...or load User Feature Module if user.role is 'user'
                case "user":
                  return import(
                    "./modules/user-dashboard/user-dashboard.module"
                  ).then((m) => m.UserDashboardModule);
              }
            })
          );
        })
      ),
  },
];

【讨论】:

    猜你喜欢
    • 2019-04-22
    • 1970-01-01
    • 2019-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-04
    • 2020-11-17
    相关资源
    最近更新 更多