canLoad в дочерних маршрутах с использованием NgRx

Я работаю в приложении 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;
  }
}

это то, что я хочу сделать с canLoad в AuthModuleGuard, но это не работает

  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

Мне нужен способ загрузить модуль приборной панели (и другие модули), только если токен в моем магазине установлен, а если нет, перенаправить на Login. Пожалуйста помоги

1 ответ

Решение

Потратив некоторое время на это, я узнал несколько очень интересных вещей:

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

Другие вопросы по тегам