如何在 Angular (angularfire2) 中使用 rxjs 运算符连接 Observables



我试图弄清楚在Angular中加入Observables的基础知识。我找到了几个例子,但似乎无法弄清楚如何让它有效地工作。

我有以下数据结构:

- courses
- $courseid1
- $courseid2
- $courseid3
- ...
- teachers
- $teacherid1
- $teacherid2
- $teacherid3
- ...
- teachersPerCourse
- $courseid1
- $teacherid1 : true
- $teacherid2 : true
- $courseid2
- $teacherid1 : true
- $courseid3
- $teacherid3 : true
- ...

现在我使用以下函数来检索带有其 slug 的课程:

getFullCourseBySlug(slug:string) {
return this.db.list('academy/courses', {
query: {
orderByChild: 'slug',
equalTo: slug
}
}).map((courses) => courses[0]);
}

现在,我希望将教师数组映射到课程对象,以便最终得到一个发出如下内容的可观察量:

{
title: ...,
slug: ...,
teachers: {
$teacherid1: {
firstname: ...,
lastname: ...,
etc ... },
$teacherid2: {
firstname: ...,
lastname: ...,
etc ... }
}
}

我在其他地方也有同样的原则,但我必须在地图函数中使用订阅,或者在可观察量中使用可观察量。例如:

getCoursesPerCategory(categoryid:string):Observable<Course[]> {
return this.db.list(`academy/coursesPerCategory/${categoryid}`).map(courses => {
courses.map(course => {
this.db.object(`academy/courses/${course.$key}`).subscribe((data) => {
course.$value = data;
});
return course;
});
return courses;
});
}

如果有人能帮我举个例子来说明如何更有效地做到这一点,那将不胜感激!

更新:

我已经设法让它工作,但我仍然无法摆脱嵌套的可观察量,这意味着我必须使用 tripple 异步管道(这可能不是最佳实践:P)我已经尝试按照建议使用 flatMap,但还不知道该怎么做。我现在有什么:

getFullCourseBySlug(slug:string): Observable<any> {
return this.db.list('academy/courses', {
query: {
orderByChild: 'slug',
equalTo: slug
}
}).map(courses => courses[0]).switchMap(course => {
course.teachers = this.db.list(`academy/teachersPerCourse/${course.$key}`)
.map(teachers => {
teachers.map(teacher => {
teacher.$value = this.db.object(`academy/teachers/${teacher.$key}`);
});
return teachers;
});
return  Observable.of(course);
});
}

使用以下模板:

{{ (course | async)?.title }}
<ul>
<li *ngFor="let teacher of ((course | async)?.teachers ) | async">
{{ (teacher.$value | async)?.firstname }}
</li>
</ul>

更新 2:

查看我的解决方案答案

我要做的是以下几行。

首先,我将构建查询课程的方法,类似于您的

课程
getFullCourseBySlug(slug:string) {
return this.db.list('academy/courses', {
query: {
orderByChild: 'slug',
equalTo: slug
}
}).map((courses) => courses[0]);
}

然后,我将创建您需要执行的第二个查询,即获取课程的教师 ID 的查询。

查询的代码可能如下所示

getTeacherIdsPerCourse(course: any) {
return this.db.list('academy/teachersPerCourse', {
query: {
orderByChild: 'slug',
equalTo: course.slug
}
}).map((teacherIds) => [teacherIds, course]);
}

看看第二个查询是为了返回 teacherId 和作为参数传递的课程而构建的事实。 现在,您可以使用 switchMap 运算符连接这两个查询。 串联将是这样的

this.getFullCourseBySlug(slug)
.switchMap(course => this.getTeacherIdsPerCourse(course))
.map(([teacherIds, course]) => {
// here you have the TeacherIds and the course and therefore you
// need to fetch from DB the details for each teacherId and build the 
// full course object with all the details
})

现在的问题是如何执行一堆查询并管理生成的可观察量,因为您需要从教师集合中读取其 ID 已通过第二个查询获取的所有教师。

首先构建您需要的第三个查询,即从教师 ID 开始获取教师所有详细信息的查询。代码可能看起来像

getTeacher(id:string) {
return this.db.list('academy/teachers', {
query: {
orderByChild: 'slug',
equalTo: slug
}
}).map((teachers) => teachers[0]);
}

如果你可以接受http调用的异步返回,即如果你不需要等待最后一个老师被获取,那么你可能会有这样的东西

this.getFullCourseBySlug(slug)
.switchMap(course => this.getTeacherIdsPerCourse(course))
.map(([teacherIds, course]) => {
for(let id of teacherIds) {
this.getTeacher(id).subscribe(teacher => {
course.teachers.push(teacher);
}
}
})

相反,如果您需要返回完整的课程及其所有教师及其所有详细信息,那么您需要做一些更复杂的事情,因为您需要等待getTeacher()方法返回的所有可观察量得到解决。

这样做的代码可能是这样的

this.getFullCourseBySlug(slug)
.switchMap(course => this.getTeacherIdsPerCourse(course))
.map(([teacherIds, course]) => {
const arrayOfObservables = new Array<Observable>();
for(let id of teacherIds) {
arrayOfObservables.push(this.getTeacher(id)
.map(teacher => course.teachers.push(teacher)));
}
return Observable.combineLatest(arrayOfObservables):
})

免责声明:我不完全确定代码是否正确,因为我没有构建真正的测试,而只是简单地组装了我认为相关的概念 - 我希望无论如何我给了你一个想法

Picci的回答帮助我朝着正确的方向前进,这是我的最终解决方案:

getTeachersPerCourse(courseid:string):Observable<Teacher[]> {
return this.db.list(`academy/teachersPerCourse/${courseid}`)
.switchMap( (teachers) => {
let teacherObservables = [];
for(let teacher of teachers){
teacherObservables.push(this.db.object(`academy/teachers/${teacher.$key}`));
}
return Observable.combineLatest(teacherObservables);
});
}
getFullCourseBySlug(slug:string): Observable<any> {
return this.db.list('academy/courses', {
query: {
orderByChild: 'slug',
equalTo: slug
}
}).map(courses => courses[0]).switchMap(course => {
return Observable.combineLatest(Observable.of(course), this.getTeachersPerCourse(course.$key), (fullCourse, teachers) => {
fullCourse['teachers'] = teachers;
return fullCourse;
});
});
}

这给了我一个课程的流,以及作为课程子级的一系列老师:

{
title: ...,
slug: ...,
teachers: [
{
firstname: ...,
lastname: ...,
etc ... 
},
{
firstname: ...,
lastname: ...,
etc ... 
}
]
}

可观察量在任何孩子的任何变化时都会发出,这正是我想要的。而且我只需要在我的模板中使用一个异步管道:

{{ (course | async)?.title }}
<ul>
<li *ngFor="let teacher of (course | async)?.teachers">
{{ teacher.firstname }}
</li>
</ul>

希望这对其他人有所帮助:)

最新更新