web-dev-qa-db-ja.com

Angular2 canActivate()非同期関数を呼び出す

Angular2ルーターガードを使用して、アプリの一部のページへのアクセスを制限しようとしています。 Firebase Authenticationを使用しています。ユーザーがFirebaseでログインしているかどうかを確認するには、FirebaseAuthオブジェクトの.subscribe()をコールバックで呼び出す必要があります。これはガードのコードです:

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

ガードが設定されているページに移動すると、authenticatedまたはnot authenticatedのいずれかがコンソールに出力されます(firebaseからの応答を待ってから少し遅れて)。ただし、ナビゲーションは完了しません。また、ログインしていない場合は、/loginルートにリダイレクトされます。したがって、私が抱えている問題は、return trueが要求されたページをユーザーに表示しないことです。これはコールバックを使用しているためだと思いますが、それ以外の方法を理解することはできません。何かご意見は?

52
Evan Salter

canActivateは、次を完了するObservableを返す必要があります。

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

returnがなく、map()の代わりにsubscribe()を使用します。これは、subscribe()SubscriptionではなくObservableを返すためです

87

canActivateは、Promiseも解決するbooleanを返すことができます

13
paulsouche

Observableを使用して、非同期ロジック部分を処理できます。たとえば、テストするコードは次のとおりです。

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

最も人気のある回答を展開します。 AngularFire2のAuth APIには多少の変更があります。これは、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;
    });
  }
}

注:これはかなり単純なテストです。 Userインスタンスをコンソールログに記録して、ユーザーのより詳細な側面に対してテストするかどうかを確認できます。しかし、少なくとも、ログインしていないユーザーのルートからルートを保護するのに役立つはずです。

5
Rudi Strydom

AngularFireの最新バージョンでは、次のコードが機能します(ベストアンサーに関連)。 「パイプ」メソッドの使用に注意してください。

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

@Injectable({
  providedIn: 'root'
})
export class AuthGuardService implements CanActivate {

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

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    return this.afAuth.authState.pipe(
      map(user => {
        if(user) {
          return true;
        } else {
          this.router.navigate(['/login']);
          return false;
        }
      })
    );
  }
}
4
Ajitesh

約束としてtrue | falseを返すことができます。

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

     })
   })
  }
}
4
rogue lad