Search code examples
angularangular2-observablesangular2-guardscanactivate

Multiple Guards calling async function


I have two Guards, Guard1 and Guard2. Guard1 returns an Observable and Guard2 returns Boolean.

canActivate: [Guard1, Guard2]

Assuming that Guard2 will return false, is the request from Guard1 automatically cancelled? or the call will be executed anyway?


Solution

  • The request from Guard1 will be cancelled, and Guard2 is kind of responsible for that. I compare it to an if statement where you combine conditions with &&, except that the order of execution/result in case of Guards is not defined (due to the fact that some Guards could be async and some not - and you want them to run in parallel, most of the time). If any condition/Guard returns false the overall result is false, there is no need to evaluate/wait for any other condition/Guard if one of them already returned false, so running requests will be cancelled.

    There are ways to actually control this, if you depend on all Guards (requests fired from Guards) to execute till they are all finished - but then they do more than just guarding a route, keep that in mind. I will explain this in more detail using my situation, when I ran into this behavior:

    I created a simple HTTP cache to keep the number of requests low. I had one request fired only within one Guard, but that request never finished because other Guards returned false earlier and the request got cancelled. This request never got cached because I did not get any result, because the request always got cancelled (simplified version that sounds useless, but it demonstrates what I want to say). Then I did this:

    • I created a "wrapper" Observable that the Guard returnes as his result
    • this "wrapper" Observable just passes the result of the request (a second Observable) back, as the result of the Guard
    • Angular will only "cancel" the returned "wrapper" Observable, so the request will finish even if the "wrapper" Observable will be cancelled

    like this:

    const wrapperSubject = new Subject<boolean>();
    this.http.get(apiUrl, this.reqOptions)
      .map(res => {
         // extract data from result ...
         // add to cache ...
         return booleanGuardResult;
       })
      .takeUntil(wrapperSubject)
      .subscribe(booleanGuardResult=> {
        wrapperSubject.next(booleanGuardResult);
        wrapperSubject.complete();
       });
    return = wrapperSubject.asObservable();
    

    This worked for my situation, the request got cached.

    If you depend on the order of the async Guard execution, I can imagine to create ONE simple Guard that just takes care of that, combining multiple other Guards to be executed in a defined order (with the usage of RxJS combineLatest and/or similar operators. there are many, depends on what you want to achieve...). If all Guards return boolean and not Observable or Promise, the order of the Guard execution is the one defined at the route - I can't imagine that Angular will change the order of execution.