typescript - canactivatechild - Angular2 canActivate() llamando a la función asíncrona
canactivate route angular (6)
Estoy tratando de usar protectores de enrutador Angular2 para restringir el acceso a algunas páginas en mi aplicación.
Estoy usando la autenticación de Firebase.
Para verificar si un usuario ha iniciado sesión con Firebase, tengo que llamar a
.subscribe()
en el objeto
FirebaseAuth
con una devolución de llamada.
Este es el código para el guardia:
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;
});
}
}
Cuando se navega a una página que tiene la protección,
authenticated
o
not authenticated
se imprime en la consola (después de un retraso esperando la respuesta de firebase).
Sin embargo, la navegación nunca se completa.
Además, si no estoy conectado, soy redirigido a la ruta
/login
.
Entonces, el problema que tengo es que
return true
no muestra la página solicitada al usuario.
Supongo que esto se debe a que estoy utilizando una devolución de llamada, pero no puedo encontrar la manera de hacerlo de otra manera.
¿Alguna idea?
En la versión más reciente de AngularFire, el siguiente código funciona (relacionado con la mejor respuesta). Tenga en cuenta el uso del método "tubería".
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;
}
})
);
}
}
Para ampliar en la respuesta más popular. La API de autenticación para AngularFire2 tiene algunos cambios. Esta es una nueva firma para lograr 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;
});
}
}
Nota: Esta es una prueba bastante ingenua. Puede consolar el registro de la instancia de Usuario para ver si desea probar con algún aspecto más detallado del usuario. Pero al menos debería ayudar a proteger las rutas contra los usuarios que no han iniciado sesión.
Puede devolver verdadero | falso como una promesa.
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);
}
}
})
})
}
}
Puede usar
Observable
para manejar la parte lógica asíncrona.
Aquí está el código que pruebo, por ejemplo:
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);
});
}
}
}
canActivate
necesita devolver un
Observable
que complete:
@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
}
}
Falta un
return
y uso
map()
lugar de
subscribe()
porque
subscribe()
devuelve una
Subscription
no un
Observable
canActivate
puede devolver una
Promise
que resuelve un valor
boolean
también