4

我正在使用 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'
}];

这是我的 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;
  }
}

这是我想要在 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;
          }
        })
      );
  }

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

{
  path: '',
  component: AppLayoutComponent,
  canLoad: [ AuthModuleGuard ],
  children: [ ... ]
}

在此处输入图像描述

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

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

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

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

4

1 回答 1

3

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

  • 如果您同时拥有loadChildrenchildren在您的路线配置中,则将选择后者
if (route.children) {
  // The children belong to the same module
  return of(new LoadedRouterConfig(route.children, ngModule));
}

  if (route.loadChildren) { /* ... */ }

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

{
  path: '',
  component: AppLayoutComponent,
  canLoad: [ AuthModuleGuard ],
  children: [ ... ]
}

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

  • 你应该注意什么时候从你的后卫那里重定向

    使用这样的配置:

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

和这样的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;
          }
      })
  );
}

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

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

最终,将达到以下几行:

this.router.navigate(['session/signin']);
return false;

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

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

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: {}};
}

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

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

{matched: false, consumedSegments: [], lastChild: 0, positionalParamSegments: {}}

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

StackBlitz

于 2020-05-17T09:01:41.883 回答