canLoad 使用 NgRx 加载子路由

Ser*_*dez 5 angular-routing ngrx angular ngrx-store angular-canload

我正在 Angular 9 应用程序中工作,并且仅当我的应用程序状态 (ngrx) 具有属性 != null 时,我才尝试加载(或不加载)特定模块

首先,我的路线中有一个 AuthGuard,但带有 canActivate。所以我希望“仪表板”模块仅在 mt AppState 有令牌时加载

这是我的路线文件

const routes: Routes = [
{
  path: '',
  component: AppLayoutComponent,
  canActivate: [ AuthGuard ],
  children: [
    { path: '',  loadChildren: () => import('./pages/modules/dashboard/dashboard.module').then(m => m.DashboardModule) }
  ]
},
{
  path: '',
  component: AuthLayoutComponent,
  children: [
    { path: 'session',  loadChildren: () => import('./pages/modules/session/session.module').then(m => m.SessionModule) }
  ]
},
{
  path: '**',
  redirectTo: 'session/not-found'
}];
Run Code Online (Sandbox Code Playgroud)

这是我的 AuthGuard。它 localestorage 没有会话,然后重定向到登录页面。

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private router: Router, public authService: AuthService) {}


  public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    if (localStorage.getItem('session')) {
        // logged in so return true
        return true;
    }

    // not logged in so redirect to login page with the return url
    this.router.navigate(['session/signin']);
    return false;
  }
}
Run Code Online (Sandbox Code Playgroud)

这就是我想要用 AuthModuleGuard 中的 canLoad 做的事情,但这不起作用

  public canLoad(): Observable<boolean> {
    return this.store.select('session').pipe(
      take(1),
      map((authstate) => {
          console.log('Token status', authstate.token);
          if (authstate.token !== null) {
              return true;
          } else {
              this.router.navigate(['session/signin']);
              return false;
          }
        })
      );
  }
Run Code Online (Sandbox Code Playgroud)

如果我这样做...应用程序会给我错误并仍然加载这两个文件

{
  path: '',
  component: AppLayoutComponent,
  canLoad: [ AuthModuleGuard ],
  children: [ ... ]
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

如果我这样做......应用程序永远不会完成加载

{ path: '', canLoad: [ AuthModuleGuard ], loadChildren: () => import('./pages/modules/dashboard/dashboard.module').then(m => m.DashboardModule) },
Run Code Online (Sandbox Code Playgroud)

这是一个 STACKBLITZ 示例(包括我的文件夹结构)---> https://stackblitz.com/edit/angular-ivy-nasx7r

仅当我的商店中的令牌已设置时,我才需要一种加载仪表板模块(和其他模块)的方法,如果未设置,则重定向到登录。请帮忙

And*_*tej 4

花了一些时间之后,我学到了一些非常有趣的东西:

if (route.children) {
  // The children belong to the same module
  return of(new LoadedRouterConfig(route.children, ngModule));
}

  if (route.loadChildren) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

这也表明canLoad在这种情况下是多余的:

{
  path: '',
  component: AppLayoutComponent,
  canLoad: [ AuthModuleGuard ],
  children: [ ... ]
}
Run Code Online (Sandbox Code Playgroud)

因为这个路由守卫与 一起使用时才有效loadChildren。

  • 你应该注意何时从你的守卫处转向

    使用这样的配置:

{
  path: '',
  component: AppLayoutComponent,
  children: [
    { 
      path: '', 
      loadChildren: () => import('./pages/modules/dashboard/dashboard.module').then(m => m.DashboardModule),
      canLoad: [AuthModuleGuard]
    }
  ]
},
Run Code Online (Sandbox Code Playgroud)

还有一个canLoad这样的守卫:

canLoad(route: Route, segments: UrlSegment[]): Observable<boolean> {
  return this.store.select('session').pipe(
      take(1),
      map((authstate) => {
          console.log('Token status', authstate.token);
          if (authstate.token !== null) {
              return true;
          } else {
              this.router.navigate(['session/signin']);
              return false;
          }
      })
  );
}
Run Code Online (Sandbox Code Playgroud)

你会陷入无限循环。当应用程序首次加载时,它将以深度优先的方式遍历每个配置,并将路径与当前段(最初为)进行比较segments = []。

但请记住,如果路线有children属性,它将遍历每个属性并查看这些段是否与路线匹配。由于子路由有path: '',它将匹配任何段,并且因为它有loadChildren,它将调用canLoad守卫。

最终,将达到这一行:

this.router.navigate(['session/signin']);
return false;
Run Code Online (Sandbox Code Playgroud)

this.router.navigate(['session/signin']);表示重定向,这意味着它将重复上面描述的步骤。


我想出的解决方案是pathMatch: 'full'向您的子路线添加一条:

{
  path: '',
  component: AppLayoutComponent,
  children: [
    { 
      path: '', 
      pathMatch: 'full',
      loadChildren: () => import('./pages/modules/dashboard/dashboard.module').then(m => m.DashboardModule),
      canLoad: [AuthModuleGuard]
    }
  ]
},
Run Code Online (Sandbox Code Playgroud)

当应用程序加载时,段将是一个空数组,并且因为path: ''匹配任何段组并且该段组[]最初是匹配的,所以将会有一个匹配:

if (route.path === '') {
  if ((route.pathMatch === 'full') && (segmentGroup.hasChildren() || segments.length > 0)) {
    return {matched: false, consumedSegments: [], lastChild: 0, positionalParamSegments: {}};
  }

  return {matched: true, consumedSegments: [], lastChild: 0, positionalParamSegments: {}};
}
Run Code Online (Sandbox Code Playgroud)

这意味着将调用守卫,并且if将到达并this.router.navigate(['session/signin'])调用 的替代块。

下次进行比较时,这些段将(大致)['session', 'signin']并且不会匹配,因为返回的是:

{matched: false, consumedSegments: [], lastChild: 0, positionalParamSegments: {}}
Run Code Online (Sandbox Code Playgroud)

如果没有匹配,它将继续搜索,直到找到东西,但不会再次调用守卫。

堆栈闪电战