Angular child route Guard blocks parent route from loading

288 views Asked by At

I have routes:

const routes: Routes = [
  {
    path: '',
    component: ConfigManagerComponent,
    canActivate: [authGuard],
    children: [
      { path: ':configName', component: ConfigDetailsComponent, canActivate: [configValidationGuard] },
      ...
    ]
  }
];

and Guard:

export const configValidationGuard = () => {
  const store = inject(Store);
  const api = inject(ConfigApiService);
  const router = inject(Router);

  return store.select(selectActiveConfig).pipe(
    withLatestFrom(api.isConfigsLoaded$),
    filter(([_, isLoaded]) => isLoaded),
    map(config => {
      if (!config) return router.createUrlTree(['no-config']);
      return true;
    })
  );
};

The primary purpose of this guard is to prevent the loading of the ConfigDetailsComponent when the configName is incorrect. To achieve this, I've implemented a selector called selectActiveConfig, which select config by its name from the store. However, before using this selector, the store needs to be populated with data, which is why I've introduced the isLoaded flag. I set isLoaded to true when I execute the loadConfigsRequest action, which I dispatch in the ConfigManagerComponent component.

Effect:

  loadConfigs$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(configsActions.loadConfigsRequest),
      switchMap(() =>
        this.api.getAllConfigs().pipe(
          map(configs => configsActions.loadConfigsSuccess({ configs })),
          tap(() => this.api.setConfigsLoaded()),
          catchError(err => of(configsActions.loadConfigsFail({ err })))
        )
      )
    );
  });

Service:

export class ConfigApiService {
  private isConfigsLoadedSubject$ = new BehaviorSubject<boolean>(false);
  isConfigsLoaded$ = this.isConfigsLoadedSubject$ as Observable<boolean>;

  setConfigsLoaded() {
    this.isConfigsLoadedSubject$.next(true);

.....
  }

Strangely, it seems that the component ConfigManagerComponent isn't even being instantiated when the configValidationGuard is in place. Interestingly, when I remove the configValidationGuard, everything starts working as expected. It appears that the guard for child routes is somehow affecting the parent route. Any insights on this issue would be greatly appreciated.

I've tried to use Guard resolver but the result is the same

1

There are 1 answers

0
Jineapple On

When you navigate to the child route, first the guard on the parent route and then the guard on the child route is run. Both have to return true for the components to be instantiated. If one returns false, navigation is cancelled.

Here is some information on how guards works with parent/child routes: https://timdeschryver.dev/blog/the-difference-between-the-canactivate-and-canactivatechild-guards#the-differences

You can't use information in the guards that are only available after instantiation of the components under the guarded route.

One option would be to create a seperate guard for the parent, a AuthAndLoadConfigGuard that first does your current auth checks and then executes the loadConfig request and returns the isConfigsLoaded observable. The child guard should only run after the parent guard has resolved successfully.