Angular 2 - Routing - CanActivate work with Observable
I have an AuthGuard (used for routing) that implements CanActivate.
canActivate() {
return this.loginService.isLoggedIn();
}
My problem is, that the CanActivate-result depends on a http-get-result - the LoginService returns an Observable.
isLoggedIn():Observable<boolean> {
return this.http.get(ApiResources.LOGON).map(response => response.ok);
}
How can i bring those together - make CanActivate depend on a backend state?
# # # # # #
EDIT: Please note, that this question is from 2016 - a very early stage of angular/router has been used.
# # # # # #
You should upgrade "@angular/router" to the latest . e.g."3.0.0-alpha.8"
modify AuthGuard.ts
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private loginService: LoginService, private router: Router) {}
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.loginService
.isLoggedIn()
.map((e) => {
if (e) {
return true;
}
})
.catch(() => {
this.router.navigate(['/login']);
return Observable.of(false);
});
}
}
If you have any questions, ask me!
Updating Kery Hu's answer for Angular 5+ and RxJS 5.5 where the catch
operator is deprecated. You should now use the catchError operator in conjunction with pipe and lettable operators.
import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { catchError, map} from 'rxjs/operators';
import { of } from 'rxjs/observable/of';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private loginService: LoginService, private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.loginService.isLoggedIn().pipe(
map(e => {
if (e) {
return true;
} else {
...
}
}),
catchError((err) => {
this.router.navigate(['/login']);
return of(false);
})
);
}
}
canActivate() accepts Observable<boolean>
as returned value. The guard will wait for the Observable to resolve and look at the value. If 'true' it will pass the check, else ( any other data or thrown error ) will reject the route.
You can use the .map
operator to transform the Observable<Response>
to Observable<boolean>
like so:
canActivate(){
return this.http.login().map((res: Response)=>{
if ( res.status === 200 ) return true;
return false;
});
}
I've done it in this way:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.userService.auth(() => this.router.navigate(['/user/sign-in']));}
As you can see I'm sending a fallback function to userService.auth what to do if http call fails.
And in userService I have:
import 'rxjs/add/observable/of';
auth(fallback): Observable<boolean> {
return this.http.get(environment.API_URL + '/user/profile', { withCredentials: true })
.map(() => true).catch(() => {
fallback();
return Observable.of(false);
});}