【问题标题】:How to join Observables with rxjs operators in Angular (angularfire2)如何在 Angular (angularfire2) 中将 Observables 与 rxjs 运算符结合起来
【发布时间】:2017-06-14 15:27:56
【问题描述】:

我试图弄清楚在 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,这意味着我必须使用三重异步管道(这可能不是最佳实践: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:

查看我的解决方案的答案

【问题讨论】:

    标签: angular dictionary rxjs observable angularfire2


    【解决方案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
        })
    

    现在的问题是如何执行一堆查询并管理生成的 observables,因为您需要从教师集合中读取其 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);
             }
          }
        })
    

    相反,如果您需要返回完整课程及其所有老师及其所有详细信息,那么您需要做一些更复杂的事情,因为您需要等待 @ 返回的所有可观察对象987654326@方法待解决。

    执行此操作的代码可能类似于

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

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

    【讨论】:

    • 这确实帮助我朝着正确的方向前进,最后我设法让它按我的意愿工作!非常感谢您的广泛回答和帮助。我会为其他人发布我的最终解决方案
    【解决方案2】:

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

    Observables 会在任何孩子发生任何变化时发出,这正是我所寻找的。而且我只需要在我的模板中使用一个异步管道:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-06
      相关资源
      最近更新 更多