在NgRx中如何使用canLoad在子路由中加载组件

5

我正在开发一个Angular 9应用程序,并尝试仅在我的应用状态(ngrx)具有属性!= null时加载特定模块。

首先,在我的路由中有一个AuthGuard,但使用canActivate。因此,我希望仅在我的AppState拥有一个令牌时才加载“dashboard”模块。

这是我的路由文件:

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。如果本地存储没有会话,则重定向到登录页面。

@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

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


canLoad 要么是包含 loadChildren 的路由的属性,要么可以在父级路由中,并使用 canLoadChildren - Aluan Haddad
@AluanHaddad 谢谢你的回答。不幸的是,我认为 canLoadChildren 在 @angular/router 工具中不存在。 - Sergio Mendez
你说得对。我很傻。 - Aluan Haddad
canLoad 看起来没问题。问题可能出在其他地方,请你创建一个 StackBlitz demo? - Andrei Gătej
@AndreiGătej 我刚刚更新了问题,并提供了一个 StackBlitz 示例 https://stackblitz.com/edit/angular-ivy-nasx7r - Sergio Mendez
1个回答

4

在此过程中,我学到了一些非常有趣的东西:

  • 如果你在路由配置中同时有 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]
    }
  ]
},

应用程序加载时,将是一个空数组,因为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: {}};
}

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

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

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

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

StackBlitz


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接