如何在Angular中使用rxjs运算符加入Observable(angularfire2)

时间:2017-06-14 15:27:56

标签: angular dictionary rxjs observable angularfire2

我试图弄清楚在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:

请参阅我的解决方案

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 ... 
  }
  ]
}

希望这有助于其他人:)