简体   繁体   English

RxJS:订阅前修改 Observable 数组

[英]RxJS: Modify Observable array before subscribing

I'm fetching data ( students via getStudents() ) from an API which returns an Observable.我正在从返回 Observable 的 API 中获取数据( students通过getStudents() )。 Within this result I need to get data from two different tables and combine the result.在这个结果中,我需要从两个不同的表中获取数据并组合结果。

Here are my simplified interfaces:这是我的简化界面:

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
}

I now need to fetch all students and add the respective school and classroom for each student via the foreign keys school_id and classroom_id .我现在需要获取所有students ,并通过外键school_idclassroom_id ID 为每个student添加各自的schoolclassroom

My current approach is something like the following.我目前的方法如下所示。 I know it's unfinished but I can't manage to find the right operator and how to properly use it.我知道它尚未完成,但我无法找到合适的操作员以及如何正确使用它。

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

All of the methods ( getStudents() , getSchool() , getClassroom() ) return Observables.所有方法( getStudents()getSchool()getClassroom() )都返回 Observables。 My goal is to get an array of students with the respective school and classroom data after subscription.我的目标是在订阅后让一系列students拥有各自的schoolclassroom数据。

I know how to get it done if I need to fetch a single student (eg with getStudent() ) and then use combineLatest to combine multiple streams.如果我需要获取单个学生(例如使用getStudent() )然后使用combineLatest组合多个流,我知道如何完成它。 But when fetching multiple students it's different.但是当获取多个students时,情况就不同了。

Thank you in advance!先感谢您!

You need to forkJoin the observable array that you get from student.map() and use map to project the result into the required object.您需要forkJoinstudent.map()获得的可观察数组,并使用map将结果投影到所需的 object 中。

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

What you can do is to covert the original array result to a stream emitting each student and the construct each record one by one.您可以做的是将原始数组结果转换为 stream 发射每个学生并一一构造每个记录。 Once the object is built, you can return it back to a single array result using toArray() .构建 object 后,您可以使用toArray()将其返回到单个数组结果。 I find doing things this way (flattening a stream that emits arrays to a stream that emits individual elements) easier to manage due to the reduced nesting.我发现以这种方式做事(将发出 arrays 的 stream 展平到发出单个元素的 stream 中)由于减少了嵌套而更易于管理。

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

This technique works best if getStudents() returns a single emission and then completes.如果getStudents()返回单个发射然后完成,则此技术效果最佳。 If the observable stays open, then toArray() will not emit as it only executes when the source observable completes.如果 observable 保持打开状态,则toArray()将不会发出,因为它仅在源 observable 完成时执行。

If it does stay open, but you only want the first array emission then adding take(1) or first() at the start of the pipe will do work.如果它确实保持打开状态,但您只想要第一个阵列发射,那么在 pipe 的开头添加take(1)first()就可以了。

StackBlitz StackBlitz

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM