Angular 2 - CanActivate Observable get value after subscription
I have a "CanActivate" check on my web service if the user is logged in.
Return "CanActivate" Observable. The only concern is that I will need to restore the value of this boolean to be able to adapt my view if the user is connected or not, without redoing the second query ...
canActivate(): Observable<boolean>|boolean {
return this.connected();
}
public connected() : Observable<boolean>{
return this.http.get(this.connectedUrl,{ withCredentials: true })
.map(this.extractBoolean)
.catch(this.handleError);
}
thanks for the help
source to share
I had a similar problem and solved it using an Authentication Service where I instead use a BehaviorSubject to track the authentication state of an item:
public member: Subject<Member> = new Subject<Member>();
private _cachedMember: Member;
private _member$: BehaviorSubject<Member>;
constructor(private _http: Http, private _router: Router) {
}
get member$(): Observable<Member> {
if (!this._cachedMember) {
this.getAuthorizedMember().subscribe();
}
return this._member$.asObservable();
}
private getAuthorizedMember(): Observable<Member> {
// call your auth API and set _cachedMember accordingly:
this._cachedMember = member;
this._member$.next(this._cachedMember);
}
My auth.guard.ts canActivate function:
canActivate(): Observable<boolean> | boolean {
return this._authService.member$.map(member => {
if (member) return true;
else {
// Set entry url
var currentUrl: URL = new URL(this._document.location.href);
this._authService.entryUrl = currentUrl.pathname;
this._router.navigate(['/']);
return false;
}
});
}
The else statement stores the request for the url, so after login the element is redirected correctly.
Then you only need to call the authentication API if _cachedMember is NULL. Remember to set _cachedMember to null for logout and report objects.
logout() {
this._cachedMember = null;
this._member$.next(this._cachedMember );
}
[EDIT] I found a problem with an earlier solution. It is a good idea to wrap the authorization mechanism in an observable and perform a null check if the _member $ object is assigned. Then the initial state is valid if you are heading directly to the route that is being guarded!
source to share
If you want to save some data when you make a request on your server to make sure the user is connected, instead of making an HTTP request in guard
, create service
one that fulfills that request (which is good practice anyway and you should do this if you want to save the query).
However, in yours service
, which is single, you can just map
call your (which results in Observable
) and store the value in service
.
For example:
@Injectable()
export class SomeService {
private userData: IUserData;
constructor(private http: Http) { }
getUserDataIfConnected() {
this.http.get('http://some-url')
.map((res: Response) => res.json() as IUserData)
.map(userData => this.userData = userData)
}
}
Then on your guard:
canActivate(): Observable<boolean> {
return this.someService.getUserDataIfConnected()
// if there was no error, we don't care about the result, just return true : The user is connected
.mapTo(true)
// if the user wasn't logged, your backend shoud return an HTTP error code in the header
// and angular HTTP service will throw an error
.catch(err => return Observable.of(false))
}
It shouldn't be any more complicated than this :)
source to share