Issue
RxJS beginner here. I am using Angular 6, and am trying to figure out how to get Observable<T> from Observable<Observable<T>>. I'm not sure if this is even valid and I'm struggling to understand it conceptually, however it seems like a simple problem.
I've looked into switchMap, flatMap, forJoin, however I don't think they fit my needs.
What I'm trying to do is an Angular route guard, which will prevent users from accessing a route unless they have the necessary permissions. 2 dependencies are a user profile to get their info from, which is then used to fetch their permissions. This mix is resulting in the Observable of Observable issue. Here's what I've got:
export class AuthPermissionsRouteGuard implements CanActivate {
constructor(
private router: Router,
private authService: AuthPermissionsService,
private openIdService: AuthOpenIdService) {}
/**Navigates to route if user has necessary permission, navigates to '/forbidden' otherwise */
canActivate(routeSnapshot: ActivatedRouteSnapshot): Observable<boolean> {
return this.canNavigateToRoute(routeSnapshot.data['permissionId'] as number);
}
/**Waits on a valid user profile, once we get one - checks permissions */
private canNavigateToRoute(permissionId: number): Observable<boolean> {
const observableOfObservable = this.openIdService.$userProfile
.pipe(
filter(userProfile => userProfile ? true : false),
map(_ => this.hasPermissionObservable(permissionId)));
// Type Observable<Observable<T>> is not assignable to Observable<T> :(
return observableOfObservable;
}
/**Checks if user has permission to access desired route and returns the result. Navigates to '/forbidden' if no permissions */
private hasPermissionObservable(permissionId: number): Observable<boolean> {
return this.permissionsService.hasPermission(permissionId).pipe(
map(hasPermission => {
if (!hasPermission) {
this.router.navigate(['/forbidden']);
}
return hasPermission;
}
));
}
}
Solution
As it stands, you are returning an Observable from the hasPermissionObservable function, which is going to be wrapped in an observable from the map operator.
You'll want to look at the mergeMap/flatMap operator or contactMap operator.
MergeMap: This operator is best used when you wish to flatten an inner observable but want to manually control the number of inner subscriptions. Example from the Learn RXJS link:
// RxJS v6+
import { of } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
// emit 'Hello'
const source = of('Hello');
// map to inner observable and flatten
const example = source.pipe(mergeMap(val => of(`${val} World!`)));
// output: 'Hello World!'
const subscribe = example.subscribe(val => console.log(val));
ContactMap: Map values to inner observable, subscribe and emit in order. Example from the Learn RXJS link:
// RxJS v6+
import { of } from 'rxjs';
import { concatMap } from 'rxjs/operators';
// emit 'Hello' and 'Goodbye'
const source = of('Hello', 'Goodbye');
// example with promise
const examplePromise = val => new Promise(resolve => resolve(`${val} World!`));
// map value from source into inner observable, when complete emit result and move to next
const example = source.pipe(concatMap(val => examplePromise(val)));
// output: 'Example w/ Promise: 'Hello World', Example w/ Promise: 'Goodbye World'
const subscribe = example.subscribe(val =>
console.log('Example w/ Promise:', val)
);
So for your example:
/**Waits on a valid user profile, once we get one - checks permissions */
private canNavigateToRoute(permissionId: number): Observable<boolean> {
const observableOfObservable = this.openIdService.$userProfile
.pipe(
filter(userProfile => userProfile ? true : false),
concatMap(_ => this.hasPermissionObservable(permissionId))); // <- try changes here
// Type Observable<Observable<T>> is not assignable to Observable<T> :(
return observableOfObservable;
}
Answered By - user10747134
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.