【问题标题】:RxJS: Modify Observable array before subscribingRxJS:在订阅前修改 Observable 数组
【发布时间】:2021-07-26 22:44:30
【问题描述】:

我正在从返回 Observable 的 API 获取数据(students 通过getStudents())。在这个结果中,我需要从两个不同的表中获取数据并组合结果。

这是我的简化界面:

export interface student Student {
   id: string
   name: string,
   school_id: string,
   classroom_id: string
}

export interface school School {
   id: string,
   name: string
}

export interface classroom Classroom {
   id: string,
   name: string
}

我现在需要获取所有students 并通过外键school_idclassroom_id 为每个student 添加相应的schoolclassroom

我目前的方法类似于以下内容。我知道它尚未完成,但我无法找到合适的运营商以及如何正确使用它。

this.getStudents().pipe(
   switchMap(students => {
      student.map(student => forkJoin([
         this.getSchool(student.school_id),
         this.getClassroom(student.classroom_id)
      ]))
   })
)

所有方法(getStudents()getSchool()getClassroom())都返回 Observables。我的目标是在订阅后获得一组students 以及相应的schoolclassroom 数据。

如果我需要获取单个学生(例如使用getStudent())然后使用combineLatest 组合多个流,我知道如何完成它。但是当获取多个students 时,情况就不同了。

提前谢谢你!

【问题讨论】:

    标签: javascript typescript rxjs observable


    【解决方案1】:

    你需要forkJoin你从student.map()获得的可观察数组,并使用map将结果投影到所需的对象中。

    const result$ = getStudents().pipe(
      switchMap((students: Student[]) =>
        forkJoin(students.map(student =>
          forkJoin([
            getSchool(student.school_id),
            getClassroom(student.classroom_id)
          ]).pipe(map(([school, classroom]) => ({
            student,
            school,
            classroom
          }))
        )
      ))
    ));
    

    【讨论】:

    • 谢谢,我有类似的想法,但如果我订阅 result$,它是空的。还通过 Angular 的异步管道进行了测试。我在这里错过了“回归”吗? getStudents() 方法本身返回值,我已经检查过了。
    • 这是一个工作演示 - stackblitz.com/edit/rxjs-phsckj
    • 现在开始工作了!我必须确保第二个 forkJoin getSchool() 和 getClassroom() 中的 Observables 仅通过使用 .pipe(take(1)) 返回一个值。
    【解决方案2】:

    您可以做的是将原始数组结果转换为发射每个学生的流,并逐个构造每个记录。构建对象后,您可以使用 toArray() 将其返回到单个数组结果。由于减少了嵌套,我发现以这种方式做事(将发出数组的流展平为发出单个元素的流)更易于管理。

    this.getStudents().pipe(
      switchMap(students => students), // emit each element individually
      concatMap(student => // construct the student object.
        forkJoin({
          classRoom: this.getClassroom(student.classroom_id),
          school: this.getSchool(student.school_id),
          student: of(student)
        })
      ),
      toArray() // when all results are built, emit one array result.
    );
    

    如果 getStudents() 返回单个发射然后完成,则此技术效果最佳。如果 observable 保持打开状态,则 toArray() 不会发出,因为它仅在源 observable 完成时执行。

    如果它确实保持打开状态,但您只想要第一个数组发射,那么在管道的开头添加 take(1)first() 就可以了.

    StackBlitz

    【讨论】:

    • +1 我喜欢这种方法。两个注意事项:1)在switchMap 中不必使用of。如果您返回数组,它将单独发出元素。 2) 我建议使用mergeMap 而不是concatMap,这样可以同时拨打多个电话。 StackBlitz
    • Ty @BizzyBob,我不知道从 switchMap 返回数组会这样做,这是一个有用的提示。
    • switchMap 像所有增肥运算符一样采用 (x: T) => Observable<U> | Promise<U> | U[] 类型的函数
    • 当使用“toArray()”时,我的订阅不会返回结果。如果我删除“toArray()”,我会得到结果,但单独而不是作为数组。但是感谢您的解决方案@DanielGimenez
    • 我假设学生只发出一个结果。如果它发出多个结果并保持打开状态,则 toArray 将永远不会发出。如果你只想要 this.getStudents 的第一次发射,那么在 switchMap 之前使用 take。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-07
    • 1970-01-01
    • 2021-02-14
    • 1970-01-01
    • 1970-01-01
    • 2021-11-16
    • 2017-10-10
    相关资源
    最近更新 更多