Search code examples
angularngrxngrx-effectsngrx-store-4.0

how to make ngrx-store working with canLoad guard


I don't know if this is a good practice or not though. But below is what I am trying to do:

I am having 2 lazy loaded modules: ManagementModule and ConfigurationModule, and the route config is as below:

const routes: Routes = [
  {path: '', redirectTo: 'management', pathMatch: 'full'}, {
    path: 'configuration',
    loadChildren: './configuration/configuration.module#ConfigurationModule',
    canLoad: [UnconfiguredGuard]
  },
  {
    path: 'management',
    loadChildren: './management/management.module#ManagementModule',
    canLoad: [ConfiguredGuard]
  }
]

Basically the idea was to check system status and redirect to different phases, eg. if sys is not configured yet, redirect to /configuration, otherwise, redirect to /management.

Before I added @ngrx/store into the project, the two canLoad guard are simple:

// ConfiguredGuard:
canLoad(route) {
  return this.configService.isConfigured()
        .do((configured: boolean) => {
          if (!configured) {
            // redirect to /configuration if unconfigured
            this.router.navigate(['/configuration']);
          }
        })
}

Now I want to adopt all @ngrx/{store,effects,router-store} libs, and the above guard is then changed to:

canLoad(route) {
    this.store.dispatch(new CheckInitStatus());
    return this.store.select('initStatus')
      .filter(s => s.checked) // check only when loaded
      .map(s => s.status === 'initialized')
      .catch(() => of(false));
}

of course, the side effect is defined:

  @Effect()
  check$: Observable<InitStatusAction> = this.actions$
    .ofType<CheckInitStatus>(CHECK_INITSTATUS)
    .mergeMap(() => this.fetchInitStatus())
    .map(status => new InitStatusChecked({status, checked: true}))
    .catch(e => of(new InitStatusCheckFailure(e)));

But the navigation was not happening, there's no ROUTER_NAVIGATION action emitted. Is anything I missed here? And also I am wondering whether this is a good practice? I did find some examples of canActivate guard usage, but none for canLoad guard. Please help, thanks!


Solution

  • I finally figured it out, after a whole day of debugging! The fix is quite simple though, add first() or take(1) into the operator chain after filter(), I cannot express the feeling I have now...but I do realize that I need a mindset change for using RxJS properly.

    canLoad(route) {
        this.store.dispatch(new CheckInitStatus());
        return this.store.select('initStatus')
          .filter(s => s.checked) // check only when loaded
          .first() // or equivalently take(1)
          .map(s => s.status === 'initialized')
          .catch(() => of(false));
    }
    

    I should read the examples of canActivate more carefully, this take(1)/first() operator is all there, for example: https://toddmotto.com/preloading-ngrx-store-route-guards