我试图弄清楚在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 ... }
}
}
我有相同的原则在其他地方工作,但我必须在map函数中使用订阅,或者使用Observables中的Observables。例如:
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;
});
}
如果有人可以帮我提供一个如何更有效地做到这一点的例子,我将不胜感激!
更新:
我已经设法让它工作但我仍然无法摆脱嵌套的Observables,这意味着我必须使用tripple Async管道(这可能不是最佳实践: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:
请参阅我的解决方案
答案 0 :(得分:1)
我会做的是以下几点。
首先,我将构建查询课程的方法,类似于你的
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]);
}
看看第二个查询是为了返回teacherIds和作为参数传递的课程而构建的。 现在,您可以使用switchMap运算符连接2个查询。 连接将类似于
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
})
现在的问题是如何执行一系列查询并管理生成的observable,因为您需要从教师集合中读取所有使用第二个查询获取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);
}
}
})
相反,如果您需要将所有教师的完整课程与其所有详细信息一起返回,那么您需要做一些更复杂的事情,因为您需要等待{返回的所有可观察者{1}}要解决的方法。
这样做的代码可能类似于
getTeacher()
免责声明:我不完全确定代码是否正确,因为我没有构建真正的测试,只是汇总了我认为相关的概念 - 我希望无论如何我已经给了你一个想法
答案 1 :(得分:0)
Picci的回答帮助我朝着正确的方向发展,这是我的最终解决方案:
0.44
这给了我一个关于课程的流程,以及作为课程孩子的一系列教师:
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;
});
});
}
Observables发出任何孩子的任何变化,正是我所寻找的。我只需要在我的模板中使用一个异步管道:
{
title: ...,
slug: ...,
teachers: [
{
firstname: ...,
lastname: ...,
etc ...
},
{
firstname: ...,
lastname: ...,
etc ...
}
]
}
希望这有助于其他人:)