angular: router canActivate not working with Observer or Promise response

router canActivate feature not work with Observable response but work with simple boolean response. see this example :

canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>{ console.log('canActivate with AclService'); // if i return boolean, canActivate feature work // return true; // but if i return Observable, canActivate feature not work return Observable.create((observer:Subject<boolean>) => { observer.next(true); }); }

haw can i fix this problem ???

[x] bug report
  • Angular version: 2.0.0-rc.3
  • Browser: [all ]
  • Language: [TypeScript ]

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 4
  • Comments: 21 (3 by maintainers)

Most upvoted comments

@darwin-gautalius I think the observable needs to complete. I had more success with .take(1) at the end. That would explain why Observable.of(true) worked. Try this:

canActivate(): Observable<boolean> {
  return this.auth.map(authState => {
    if (!authState) this.router.navigate(['/login']);
    console.log('activate?', !!authState);
    return !!authState;
  }).take(1)
}

The fact that the Observable needs to complete should be documented, this wasn’t obvious to me either.

@crowmagnumb I would try the following approach: In the service that determines whether the user is admin, and which you’d be able to access from your canActivate function or guard class method, declare this:

private _isAdmin$ = new ReplaySubject(1); // this ensures that the router can always get the most recent value set, but only once it becomes available at all

get isAdmin$() { return this._isAdmin$.asObservable(); }

In your canActivate() function (or guard class method):

return mySecurityService.isAdmin$.first(); // NOTE: this still returns an observable!

And then, once the security service determines if the user has admin privileges, it would set the value:

this._isAdmin$.next(true); // or false if the user is not an admin

I’m doing something very similar, but in my case user information comes as part of page load from the server. So I have an initial value to provide, therefore I’m using BehaviorSubject instead of ReplaySubject with a capacity of 1. In a sense, these two are not very different, with the exception being that the former requires an initial value in the constructor, while the latter allows no value. I have also performed a test just now by introducing a delay in my canActivate() method, which has proved that the router can wait for a value from the observable, and once it becomes available - will decide whether to navigate to the respective route based on that value. Hope this helps!

@vicb it’s not support request, in the official doc api syntax: canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean; then canActivate returns one of this results : Observable<boolean> Or a boolean but with the first case Observable<boolean> , it is not working

Observable.of(true)

Are there any plans to have a guard properly handle as an Observable? I have a problem that I need to wait for a login to happen before telling the guard true or false. If the user goes straight to a url defined as a route say “<server>/admin” then when the canActivate() is called I don’t have the user or its rights yet. So I have to default to false. When the login check is made (which is triggered automatically) and it comes back saying “yes, they have admin rights” I set the observable to true. But by then the router has determined that no it is not accessible so the router component does not show. The user then has to click on the link manually to get to the admin page. Since the canActivate() is now true it works, but annoying that I can’t go straight to the admin page by typing in the url.

@alobakov Thank you!

I was using a BehaviourSubject and for some reason it wasn’t working.

I’m having the same problem. Here is my guard:

import { Injectable } from '@angular/core';
import { CanActivate, Router }    from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { FirebaseAuth } from 'angularfire2';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private router: Router, private auth: FirebaseAuth) { }

  canActivate(): Observable<boolean> {
    return this.auth.map(authState => {
      if (!authState) this.router.navigate(['/login']);
      console.log('activate?', !!authState);
      return !!authState;
    });
  }
}

Browser console show activate? true but the page doesn’t show. The component attached to the path is not initialized too.

I tried with return Observable.of(true); and return true; and the page shows correctly.

I don’t think canActivate support Observable<boolean> only for Observable.of(true).

btw, I use this firebase. (just for additional info) “angularfire2”: “2.0.0-beta.0”, “firebase”: “2.4.2”,

i juste done to test it with .take(1) it’s work fine,
thank’s @Denhai