typescript - Angular2 canActivate() volá asynchronní funkci
Snažím se použít ochranu tras (router guards) v Angular2 k omezení přístupu na některé stránky v mé aplikaci. Používám Firebase Authentication. Abych mohl ověřit, zda je uživatel přihlášen přes Firebase, musím zavolat .subscribe() na objektu FirebaseAuth s callback funkcí. Toto je kód pro 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;
});
}
}
Když přejdu na stránku, která má na sobě ochranu (guard), v konzoli se vytiskne buď authenticated, nebo not authenticated (po nějakém zpoždění čekajícím na odpověď z Firebase). Navigace se však nikdy nedokončí. Také, pokud nejsem přihlášen, jsem přesměrován na cestu /login. Tedy problém, který mám, je, že return true nezobrazí uživateli požadovanou stránku. Předpokládám, že je to proto, že používám zpětné volání (callback), ale nemohu přijít na to, jak to udělat jinak. Máte nějaké nápady?
Odpověď č. 1
canActivate musí vracet Observable, který se dokončí:
@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
}
}
Chybí zde return a používám map() místo subscribe(), protože subscribe() vrací Subscription, nikoli Observable
Odpověď č. 2
Můžete použít Observable pro zpracování asynchronní logiky. Zde je kód, který jsem například testoval:
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);
});
}
}
}
Odpověď č. 3
canActivate může také vrátit Promise, který vyřeší boolean
Odpověď č. 4
Můžete vrátit true|false jako promise.
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);
}
}
})
})
}
}
