0

I have a guard that checks a user's session. I am creating an observable from a promise and returning that observable.

Unfortunately the router always redirects me to homepage on refresh. I guess it has to do with the observable as when I simply navigate, it enters the if and returns the 'Observalbe.of(true)', which works.

@Injectable()
export default class SessionGuard implements CanActivate {

  constructor(private authService: AuthService) {}

  public canActivate(): Observable<boolean> | Promise<boolean> | boolean {
    if (this.authService.hasSessionChecked) {
      return Observable.of(true).take(1);
    }

    const obs = Observable.fromPromise(this.authService.getSession());
    return obs.map((session) => {
      this.authService.setUserSession(session);
      return true;
    }).take(1);
  }
}

I have tried replacing the observable with a 'return true' and everything works as expected.

Any ideas? Thanks!

Bogdan
  • 77
  • 1
  • 9

3 Answers3

0

It doesn't work because canActivate is supposed to return a boolean.

Why would you want to use an Observable ?

  • that isnt true, the definition of canActivate in the interface allows 3 return types: export interface CanActivate { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | boolean; } – Jota.Toledo Jun 16 '17 at 08:53
  • https://angular.io/api/router/CanActivate line 15 of the first example. – Bogdan Jun 16 '17 at 08:54
  • Just the line beneath it, you can see that it returns the canActivate function from permission. See what this function returns. –  Jun 16 '17 at 08:55
  • In this example, it does return a boolean. But, as you can see on line 15, it can return a promise or observable that resolve to a boolean or a boolean. – Bogdan Jun 16 '17 at 08:58
  • I will ask you to think for a second. If this function returns **only** the result of another function, then what it can return ? –  Jun 16 '17 at 08:58
  • I need the canActivate to return an observable so the route resolver will wait until the http call is completed. How can you do that by returning a boolean? – Bogdan Jun 16 '17 at 09:06
  • You call your service, and return the boolean in the subscribe. If it successes, you return true, otherwise you return false ! But returning an empty observable won't resolve it, this is why it doesn't work. –  Jun 16 '17 at 09:07
  • do something like `return this.myService.myMethod('your args').subscribe(success => true, failure => false);` –  Jun 16 '17 at 09:09
  • that would be wrong, in that case you would return a subscription, not a boolean value – Jota.Toledo Jun 16 '17 at 09:53
  • Then just lose the first return (I can't test what I'm writing, and I don't know the syntax by heart, sorry) –  Jun 16 '17 at 09:55
  • The service uses a promise. The promise is resolved but after, the router redirects me to / – Bogdan Jun 16 '17 at 10:07
  • Well it's not related to your guard anymore then. If the return works but it redirects you where you don't want, try changing the "where you don't want". –  Jun 16 '17 at 10:09
  • I thought about that. But the weird part is that if I have a simple "return true", the router works as expected. That's what made me think it's about the guard. – Bogdan Jun 16 '17 at 10:12
  • Well it's not **about** the guard, but maybe **because of** it. Maybe you get redirected to `/` if you don't resolve your Promise ? –  Jun 16 '17 at 10:13
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/146856/discussion-between-bogdan-and-trichetriche). – Bogdan Jun 16 '17 at 10:15
0

First, the definition of canActivate is the following:

export interface CanActivate {
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean;
}

So you should add the parameters in the function.

Second, if you are going to return an observable and only an observable, then change the return type to just observable. If you want a mix, then you could leave more than one return type.

Third, remove the take() operator and refactor the guard to look like this:

@Injectable()
export default class SessionGuard implements CanActivate {

  constructor(private authService: AuthService) {}

  public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
    if (this.authService.hasSessionChecked) {
      return true;
    }
    const obs = Observable.fromPromise(this.authService.getSession());
    return obs.map(session => {
      this.authService.setUserSession(session);
      return true;
    });
  }
}
Jota.Toledo
  • 27,293
  • 11
  • 59
  • 73
0

As @trichetriche suggested, I have replaced the observable with a promise and returned that.

It's working great now.

return this.authService.getSession().then(
  data => {
    this.authService.setUserSession(data);
    return true;
  },
  err => false,
);
Bogdan
  • 77
  • 1
  • 9