Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 canActivate() calling async function

I am trying to use Angular2 router guards to restrict access to some pages in my app. I am using Firebase Authentication. In order to check if a user is logged in with Firebase, I have to call .subscribe() on the FirebaseAuth object with a callback. This is the code for the guard:

import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { AngularFireAuth } from "angularfire2/angularfire2"; import { Injectable } from "@angular/core"; import { Observable } from "rxjs/Rx";  @Injectable() export class AuthGuard implements CanActivate {      constructor(private auth: AngularFireAuth, private router: Router) {}      canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>|boolean {         this.auth.subscribe((auth) => {             if (auth) {                 console.log('authenticated');                 return true;             }             console.log('not authenticated');             this.router.navigateByUrl('/login');             return false;         });     } } 

When a navigate to a page that has the guard on it, either authenticated, or not authenticated is printed to the console (after some delay waiting for the response from firebase). However, the navigation is never completed. Also, if I am not logged in I am redirected to the /login route. So, the issue I am having is return true doesn't display the requested page to the user. I'm assuming this is because I am using a callback, but I am unable to figure out how to do it otherwise. Any thoughts?

like image 584
Evan Salter Avatar asked Jul 17 '16 20:07

Evan Salter


2 Answers

canActivate needs to return an Observable that completes:

@Injectable() export class AuthGuard implements CanActivate {      constructor(private auth: AngularFireAuth, private router: Router) {}      canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>|boolean {         return this.auth.map((auth) => {             if (auth) {                 console.log('authenticated');                 return true;             }             console.log('not authenticated');             this.router.navigateByUrl('/login');             return false;         }).first(); // this might not be necessary - ensure `first` is imported if you use it     } } 

There is a return missing and I use map() instead of subscribe() because subscribe() returns a Subscription not an Observable

like image 98
Günter Zöchbauer Avatar answered Sep 18 '22 14:09

Günter Zöchbauer


You might use Observable to handle the async logic part. Here is the code I test for example:

import { Injectable } from '@angular/core'; import { CanActivate } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import { DetailService } from './detail.service';  @Injectable() export class DetailGuard implements CanActivate {    constructor(     private detailService: DetailService   ) {}    public canActivate(): boolean|Observable<boolean> {     if (this.detailService.tempData) {       return true;     } else {       console.log('loading...');       return new Observable<boolean>((observer) => {         setTimeout(() => {           console.log('done!');           this.detailService.tempData = [1, 2, 3];           observer.next(true);           observer.complete();         }, 1000 * 5);       });     }   } } 
like image 25
Luozt Avatar answered Sep 21 '22 14:09

Luozt