将Observable<Observable<T>>转换为Observable<T>

10

我是一个RxJS初学者。我正在使用Angular 6,并尝试弄清楚如何从Observable<Observable<T>>中获取Observable<T>。我不确定这是否有效,我很难理解它的概念,但似乎这是一个简单的问题。

我尝试过使用switchMap、flatMap和forJoin,但我认为它们不适合我的需求。

我正在尝试实现一个Angular路由守卫,以防止用户访问一个路由,除非他们拥有必要的权限。我需要获取用户的个人资料来获取他们的信息,然后再用这些信息来获取他们的权限。这种混合导致了Observable of Observable的问题。这是我的代码:

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;
            }
        ));
    }
}
1个回答

6

目前,您从hasPermissionObservable函数返回一个Observable,该Observable将被包装在map操作符的Observable中。

您需要查看mergeMap/flatMap操作符contactMap操作符

MergeMap: 当您希望展开内部Observable但想手动控制内部订阅数量时,最好使用此操作符。以下是来自Learn RXJS链接的示例:

// 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));

: 将值映射到内部的可观察对象中,并按顺序订阅和发出。以下是来自Learn RXJS链接的示例:
// 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)
);

所以,以您的例子为例:
/**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;
}

1
哇,非常感谢!我尝试了很多不同的映射操作符,但是我就是无法正确地理解语法等。许多 Angular 的示例都已经过时(使用较旧的 RxJS 版本),而 RxJS 文档中没有足够组合的示例来演示这个问题。再次感谢! - Ross

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接