107 votes

Angular2 canActivate () appelant une fonction asynchrone

Je suis en train d'utiliser Angular2 routeur gardes de restreindre l'accès à certaines pages de mon application. Je suis à l'aide de Firebase d'Authentification. Afin de vérifier si un utilisateur est connecté avec Firebase, j'ai appeler .subscribe() sur le FirebaseAuth objet avec une fonction de rappel. Ceci est le code de la garde:

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;
        });
    }
}

Lorsqu'un accédez à une page qui a la garde sur elle, soit en authenticatedou not authenticated est imprimé sur la console (après un certain délai d'attente de la réponse de firebase). Toutefois, la navigation n'est jamais terminé. Aussi, si je ne suis pas connecté, je suis redirigé vers l' /login route des. Donc, la question que je vais avoir est - return true ne pas afficher la page demandée à l'utilisateur. Je suppose que c'est parce que je suis en utilisant une fonction de rappel, mais je suis incapable de trouver comment faire autrement. Toutes les pensées?

145voto

Günter Zöchbauer Points 21340

canActivate doit renvoyer un Observable qui complète:

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

Il manque un return et j'utilise map() au lieu de subscribe() car subscribe() renvoie un Subscription pas un Observable

42voto

KobeLuo Points 66

Vous pouvez utiliser Observable pour gérer la partie logique asynchrone. Voici le code que je teste par exemple:

 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);
      });
    }
  }
}
 

25voto

paulsouche Points 91

canActivate peut renvoyer un Promise qui résout un boolean aussi

22voto

Rajper Points 150

Vous pouvez retourner true | false comme une promesse.

 import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {Observable} from 'rxjs';
import {AuthService} from "../services/authorization.service";

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

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
  return new Promise((resolve, reject) => {
  this.authService.getAccessRights().then((response) => {
    let result = <any>response;
    let url = state.url.substr(1,state.url.length);
    if(url == 'getDepartment'){
      if(result.getDepartment){
        resolve(true);
      } else {
        this.router.navigate(['login']);
        resolve(false);
      }
    }

     })
   })
  }
}
 

6voto

Rudi Strydom Points 522

Pour développer la réponse la plus populaire. L'API Auth pour AngularFire2 a quelque peu changé. Ceci est la nouvelle signature pour réaliser un AngularFire2 AuthGuard:

 import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { AngularFireAuth } from 'angularfire2/auth';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

@Injectable()
export class AuthGuardService implements CanActivate {

  constructor(
    private auth: AngularFireAuth,
    private router : Router
  ) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<boolean>|boolean {
    return this.auth.authState.map(User => {
      return (User) ? true : false;
    });
  }
}
 

Note: Ceci est un test assez naïf. Vous pouvez consigner l'instance de l'utilisateur pour voir si vous souhaitez tester certains aspects plus détaillés de l'utilisateur. Mais devrait au moins aider à protéger les itinéraires contre les utilisateurs qui ne sont pas connectés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X