typescript - Angular2 canActivate() asenkron fonksiyon çağırma


Uygulamamdaki bazı sayfalara erişimi sınırlamak için Angular2 yönlendirici korumalarını kullanmaya çalışıyorum. Firebase Kimlik Doğrulamasını kullanıyorum. Bir kullanıcının Firebase ile giriş yapıp yapmadığını kontrol etmek için, FirebaseAuth nesnesi üzerinde bir geri çağırma ile .subscribe() çağırmam gerekiyor. Korumaya ait kod şu şekildedir:

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

Bir guard bulunan bir sayfaya gittiğimde, konsola ya authenticated ya da not authenticated yazdırılıyor (firebase'den gelen yanıtı beklerken bir süre gecikme oluyor). Ancak, navigasyon hiçbir zaman tamamlanmıyor. Ayrıca, giriş yapmamışsam /login rotasına yönlendiriliyorum. Yani, yaşadığım sorun return true'nın kullanıcıya istenen sayfayı göstermemesi. Bunun, callback kullandığım için olduğunu varsayıyorum, ama başka şekilde nasıl yapacağımı çözemedim. Fikirleriniz var mı?

Cevap #1

canActivate, tamamlanan bir Observable döndürmelidir:

@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 eksik ve subscribe() yerine map() kullanıyorum çünkü subscribe() bir Observable değil, bir Subscription döndürür

Cevap #2

Asenkron mantık kısmını yönetmek için Observable kullanabilirsiniz. İşte örnek olarak test ettiğim kod:

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

Cevap #3

canActivate aynı zamanda boolean çözen bir Promise de döndürebilir

Cevap #4

True|false değerini bir promise olarak döndürebilirsiniz.

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

     })
   })
  }
}

İlgili makaleler