2

As the title says: Is there a way to tell the router to cancel its current navigation?

Here's the situation:

  • A route guard dispatches an action.
  • An effect performs an async call based on that action.
  • Upon completion, the async call will dispatch a store update action on a property.
  • The guard is subscribed to the selector of that property (say "property$")
  • Once that property$ updates, the guard decides to allow the navigation or not.

This approach only works when the resource requested by the effect's async action returns properly (HTTP 200). But in case the action failes (say HTTP 404), here is the issue:

  • The guard won't approve nor reject the navigation. It's pending and no other navigation request can happen until the guard returns either true or false.

Is there a way to implement a call such as, naively speaking: this._router.cancelAllNavigationRequests() ?

Jem
  • 6,226
  • 14
  • 56
  • 74
  • Hey, can´t you change your guard to redirect to a default 'error page' when the response from your HTTP call is not 200? – SrAxi Mar 06 '18 at 15:28
  • You could either indicate failure in state or have the guard listen to the actions that the effect will emit. – bygrace Mar 06 '18 at 19:47

1 Answers1

1

You did not provide any code cause your case should work without the need of cancelAllNavigationRequests you just not handling the HTTP error properly. Here is a method I used in a guard it might help or give you ideas, its similar to the one in the ngex example: This Guard will check if a Classified already exist and not partial, before making a call to the service.

@Injectable()
export class ClassifiedGuardService implements CanActivate, CanActivateChild {
  constructor(private store: Store<fromRoot.State>, private classifiedService: ClassifiedService, private router: Router) {
  }

  vendorClassifiedAlreadyLoaded(id: string): Observable<boolean> {
    return this.store.pipe(
      select(fromContext.selectAllClassifieds),
      map(entities => {
        for (let i = 0; i < entities.length; i++) {
          if (entities[i].id === id && !entities[i].partial) {
            return true;
          }
        }
        return false;
      }),
      take(1)
    );
  }
  getVendorClassifiedFromServer(id: string) {
    return this.classifiedService.getVendorsClassified({id: id}).pipe(
      map(payload => new ClassifiedAction.GetVendorsClassifiedSuccess(payload)),
      tap((action: ClassifiedAction.GetVendorsClassifiedSuccess) => this.store.dispatch(action)),
      map(payload => !!payload),
      catchError(err => {
        this.store.dispatch(new ClassifiedAction.GetVendorsClassifiedFail(err));
        this.router.navigate(['/errors/bad-request']);
        return of(false);
      })
    );
  }


  getFullClassified(id: string): Observable<boolean> {
     return this.vendorClassifiedAlreadyLoaded(id).pipe(
        switchMap(inStore => {
          if (inStore) {
            return of(inStore);
          }
          return this.getVendorClassifiedFromServer(id);
        })
      );
  }

  canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
    return this.getFullClassified(route.params['id']);
  }

  canActivateChild(route: ActivatedRouteSnapshot) {
    return this.canActivate(route);
  }
}
ramon22
  • 3,528
  • 2
  • 35
  • 48
  • hi thanks for your comment, but your code isn't redux based. Complexity here is that the guard has no access nor visibility to the sotre's effect and resulting action dispatching. – Jem Mar 19 '18 at 12:47