2

I've put an angular routing authguard to put my page. I've an api that checks if the token is valid and based on that I want to return true or false for further navigation.

But seems that in 'subscribe' I assign my 'result' flag true not persisting outside of the subscribe. Is 'result' variable in wrong scope here? How can I correct that? Please help me understand my mistake in below code.

canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
  const jwt = localStorage.getItem('jwt');
  let result = false;

  this.signinService.isSignedin(jwt).subscribe((res) => { // <---
    console.log(res.json().message);
    if (res.json().message === 'Token Valid') {
      result = true;
      console.log(result);
      return true;
    } else {
      result = false;
      return false;
    }
  }, (err) => { // <---
    console.log(err);
    result = false;
    return false;
  });
  console.log('END' + result);
  return result;}

The above code prints out on console in below order, which seems to be weird to me. Anyone can explain please?

ENDfalse

Token Valid

true

tyro
  • 577
  • 8
  • 17
  • 2
    You can't. That's why the method can also return a Promise or an Observable: those types allow returning a boolean **asynchronously**. If you do, the router will subscribe to the returned observable/promise, and will navigate (or not) when the boolean even is emitted. – JB Nizet Dec 21 '17 at 21:14
  • Hey, did any of the answers help you? :) – AT82 Dec 27 '17 at 10:50
  • Yes, it made me understand that I cant return boolean from and api call. So as I've stated in below comment, now I've decided to do it on jwt existence and return boolean accordingly. I will verify the token at server side only when user posts. – tyro Dec 27 '17 at 15:07

3 Answers3

1

The result value is returned before the callback function you defined is executed. Which is quite logical because the callback is called after the api round trip has finished.

Robbert Draaisma
  • 463
  • 4
  • 14
1

But seems that in 'subscribe' I assign my 'result' flag true not persisting outside of the subscribe.

I think you are not fully understanding how the interpreter will read and evaluate this code. Because the JavaScript event loop is never blocking, the interpreter will get to the subscribe block, make the request, and then continue along executing the code outside of the subscribe block without waiting for the response to come back. This is why "ENDfalse" is printed first. Then the response comes in and the callback function is executed with the line console.log(res.json().message); printing "Token Valid" and console.log(result); returning true.

One way you could fix this might be to return the whole observable and then subscribe to in the place where you actually want to use the value of "result" (and in fact your function signature of canActivate shows that you should be returning an observable, not the data).

canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
  const jwt = localStorage.getItem('jwt');
  return this.signinService.isSignedin(jwt);
}
Jim
  • 3,821
  • 1
  • 28
  • 60
  • I'm trying to return the boolean as based on what I'll redirect to correct page. Now I've decided to do it on jwt existence, and will verify the token at server side only when user posts. Having said that, your detailed feedback helped me understand non blocking events of JS. Thanks !! – tyro Dec 22 '17 at 07:27
-1

You cannot return anything from subscribe, but what you can do, is to return an Observable from your guard.

Also as pointed out by the others, this is asynchronous, therefore the order of the console logs. You can read more about it here: How do I return the response from an Observable/http/async call in angular2?

As for how to format your code, let's as mentioned return an Observable:

// add 'return' and use 'map' instead: 
return this.signinService.isSignedin(jwt).map((res) => {
  if (res.json().message === 'Token Valid') {
    return true;
  } else {
    return false;
  }
}, (err) => {
  return false;
});
AT82
  • 71,416
  • 24
  • 140
  • 167