Skip to content

router canActivate not working with Observer or Promise response #9613

Closed

Description

@aghou

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 ]

Activity

  1. DzmitryShylovich commented on Jun 26, 2016

    @DzmitryShylovich
    Contributor

    Observable.of(true)

  2. vicb commented on Jun 26, 2016

    @vicb
    Contributor

    Your question sounds like a support request.

    Please use the issue tracker only for bugs and feature requests.

    Use gitter for support request.

  3. aghou commented on Jun 26, 2016

    @aghou
    Author

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

  4. darwin-gautalius commented on Jun 27, 2016

    @darwin-gautalius

    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",

  5. jpsantosbh commented on Jun 27, 2016

    @jpsantosbh

    Same here with "@angular/core": "2.0.0-rc.2", "@angular/router": "^3.0.0-alpha.3",

  6. Denhai commented on Jun 27, 2016

    @Denhai

    @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)
    }
    
  7. aghou commented on Jun 27, 2016

    @aghou
    Author

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

  8. darwin-gautalius commented on Jul 2, 2016

    @darwin-gautalius

    thanks @Denhai. It really works as intended.

  9. PEsteves8 commented on Jul 5, 2016

    @PEsteves8

    Thanks for this. I guess angular2 just keeps waiting for it to finish. I also tried with .first(), instead of .take(1) and using observer.complete() after next and it worked as well. Not sure if there are better practices or problems with any of the approaches that one should know.

  10. philipbulley commented on Jul 9, 2016

    @philipbulley

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

  11. awerlang commented on Jul 30, 2016

    @awerlang
    Contributor

    I sent a PR fixing this issue. Take a look at #10412.

  12. crowmagnumb commented on Oct 6, 2016

    @crowmagnumb

    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 "/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.

  13. alobakov commented on Oct 19, 2016

    @alobakov

    @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!

  14. hamidhst commented on Jun 12, 2017

    @hamidhst

    @alobakov Just Perfect!
    After wasting a whole day, you saved it, thanks a lot. (I'm using Angular v4.)

  15. yo-epicdavid commented on Jun 27, 2017

    @yo-epicdavid

    @alobakov Thank you!

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

  16. raysuelzer commented on Oct 20, 2017

    @raysuelzer

    It would be great if there was a way to have it work without requiring the observable to complete. Using a AsyncSubject or BehaviorSubject allows throttling of any Ajax requests made by calling canActivate.

  17. gang-qiu commented on Jul 18, 2018

    @gang-qiu

    @Snesi Was also not able to get Behavior subject to work. Replay subject works though. Using angular 5 and the same overall implementation as @alobakov suggested but for BehaviorSubject

  18. prashuMishra commented on Oct 18, 2018

    @prashuMishra

    i have the same issue.

    app.route.ts.

    export const ROUTES: Routes = [

    {
    path: '',
    loadChildren: 'app/login/login.module#LoginModule',
    },
    {
    path: 'home',
    loadChildren: 'app/home/home.module#HomeModule',
    canActivate: [AuthGuard]
    },
    { path: '**', redirectTo: '' },
    ];

    authguard.ts

    import { Injectable } from '@angular/core';
    import { Router, CanActivate } from '@angular/router';
    import { AuthService } from './auth.service';
    import { Observable } from 'rxjs/Observable';
    @Injectable()
    export class AuthGuard implements CanActivate {
    constructor(public auth: AuthService, public router: Router) {}
    canActivate(): Observable {
    console.log('Canactivate Called');
    return Observable.of(true).take(1);
    }
    }

    above code should show "Canactivate Called" in console. but is not working.

  19. RazvanSebastian commented on Apr 12, 2019

    @RazvanSebastian

    Inside AuthGuard I have the following

       // Guard method
        canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
         
            return this._authService.checkTokenAvailability().pipe(
                map((response: Response) => {
                   // return Observable with true value and let the router and the guard to redirect you to dashboard in my case
                    return true
                }),
                catchError((err: Response) => {
                   // navigate to login page
                    this._router.navigateByUrl('/admin');
                    // handle the error by throwing statusText into the console
                   return throwError(err.statusText);
                })
            );
        }
    

    And somewhere into the router module you have

    const routes: Routes = [
      ...
      {
        path: 'dashboard',
        component: DashboardComponent,
        canActivate: [AuthGuardService]
      }
    ....
    ];
    
  20. yo-epicdavid commented on Apr 13, 2019

    @yo-epicdavid
  21. angular-automatic-lock-bot commented on Sep 14, 2019

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  22. locked and limited conversation to collaborators on Sep 14, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions