【问题标题】:tranforming RxJS Observable转换 RxJS Observable
【发布时间】:2019-03-24 05:40:34
【问题描述】:

我使用 angularFirestore 在 firebase 上进行查询,我想使用 DocumentReference 连接来自多个文档的数据。

管道中的第一个运算符映射返回一个IOrderModelTable数组,第二个运算符,即switchMap迭代数组,每个元素使用每个元素中包含的id来查询其他表中的数据。

问题在于,在 swithMap 中,由于动画地图运算符,我获得了一个可观察的数组。如何获取 IOrderModelTable 数组,然后返回该数组的 observable。

代码是:

getDataTableOperatorsFromDB(): Observable<IOrderModelTable[]> {
  const observable = this.tableOperatorsCollectionsRef.snapshotChanges().pipe(
    map(actions => {
      return actions.map(a => {
        const data = a.payload.doc.data() as IOrdersModelDatabase;
        const id = a.payload.doc.id;

        data.ot = id;
        return data;
      });
    }),
    switchMap(data => {
      const result = data.map(element => {
        return this.afs.collection('Orders/').doc(element.orderNumberReference.id).valueChanges().pipe(map(order => {
          return {
            otNumber: element.ot,
            clientName: '',
            clientReference: order.clientReference,
            id: element.orderNumberReference,
          };
        }));
      });

      // Result must be an IOrderModelTable[] but is a Observable<IOrderModelTable>[]

      return of(result);
    })
  );

【问题讨论】:

  • 当你使用switchMap时,你需要输出一个observable。因此,您可以将 switchMap 替换为另一个运算符(如 map、concat 等)。或者,您可以将可观察到的排放转换为列表,为此使用toArray()。非常重要:如果您的 observable 没有终止,则 toArray 将不起作用。不过,我不确定是否能很好地理解您的问题,一些解释将有助于“为什么使用 switchMap”或“您的 observable 发出几次,还是只发出一次”?
  • @madjaoue observable 永远不会完成并发出一个包含来自 firebase 的所有文档的唯一数组,我想将该数组转换为每个元素的其他文档的查询。假设 observable 返回一个对象数组,每个对象包含一个订单号和客户的 id,我需要获取客户的名称,为此我使用 id,并与订单号结合,这样我就有了一个对象数组,每个对象包含一个客户名称和订单号。
  • 我发了一个答案,你可以试试吗?

标签: javascript angular rxjs


【解决方案1】:

@madjaoue 你是对的,在这种情况下,mergeMap 是正确的运算符,因为对于每个发出的事件,内部 observable 都会被销毁,所以在订阅中你只会得到最终发出的事件,即最后一行。这个 observable 是长期存在的,永远不会完成,所以也可以使用操作符 take 和包含文档列表的数组的长度。

非常感谢您的帮助。 :D

getDataTableOperatorsFromDB(): Observable<IOrderModelTable[]> {
    const observable = this.tableOperatorsCollectionsRef.snapshotChanges().pipe(
      switchMap(actions => {
        return from(actions).pipe(
          mergeMap(action => {
            console.log(action);
            const data = action.payload.doc.data() as IOrdersModelDatabase;
            const otNumber = action.payload.doc.id;
            return this.afs.collection('Orders/').doc(data.orderNumberReference.id).valueChanges().pipe(map(order => {
              return {
                otNumber: otNumber,
                clientName: '',
                clientReference: order.clientReference,
                id: data.orderNumberReference,
              };
            }));
          }),
          mergeMap(order => {
            console.log(order);
            return this.afs.collection('Clients/').doc(order.clientReference.id).valueChanges().pipe(map(client => {
              return {
                otNumber: order.otNumber,
                clientName: client.name,
                clientReference: order.clientReference,
                id: order.id,
              };
            }));
          }),
          take(actions.length),
          toArray(),
          tap(console.log),
        );
      }),

【讨论】:

    【解决方案2】:

    您可以使用to Array 运算符将流转换为数组,但请确保您的流将结束。 诀窍是选择正确的流。

    对于您的问题,自然 来源将是您第一次调用时收到的列表。以一种示意图的方式,我可以说,你会得到一个 id 列表,你可以将其转换为增强信息列表:

    第一个输入...snapshopChanges()

    ----[A, B, C]----->

    每个元素都通过...valueChanges()进行转换:

    -------调用A -------------DataA-------->

    --------调用B ------------DataB----->

    --------调用C --------DataC----->

    然后使用toArray() 减少到:

    ----------------------------------------------[数据A、数据C、数据B]-------->

    代码

    getDataTableOperatorsFromDB(): Observable<IOrderModelTable[]> { {
      return this.tableOperatorsCollectionsRef.snapshotChanges()
        .pipe(
          map(actions => {
            from(data).pipe(
              map(action => {
                const data = a.payload.doc.data() as IOrdersModelDatabase;
                const id = a.payload.doc.id;
                data.ot = id;
                return data;
              }),
              mergeMap(element => {
                return this.afs.collection('Orders/').doc(element.orderNumberReference.id).valueChanges().pipe(
                  map(order => {
                    return {
                      otNumber: element.ot,
                      clientName: '',
                      clientReference: order.clientReference,
                      id: element.orderNumberReference,
                    };
                  })
                );
              }),
              toArray()
            );
             
          })
        )
    }
    

    重要提示:我将switchMap替换为mergeMap,否则可能会丢弃一些信息。

    【讨论】:

    • 既然switchMap返回一个observable,为什么不能直接订阅结果返回
    • @Pari Baker 如果你在谈论他的代码,他的函数会返回一个可观察的列表,类似于[Observable1, Observable2, Observable3,...]。所以他使用他的功能的方式是myfunction.subscribe( observables =&gt; observables.forEach( observable=&gt; observable.subscribe(element=&gt; ...))。对于这么多级别来说,这是一个坏主意。如果他有一个包含 1000 个元素的列表,他将必须创建 1000 个 Observables + 订阅每个元素。此外,连接 1000 个订阅对于竞争条件可能很棘手。这个想法是:使用正确的运算符,并且最少的努力......
    猜你喜欢
    • 2021-05-24
    • 2023-03-23
    • 1970-01-01
    • 2016-12-25
    • 2018-02-19
    • 2021-04-11
    • 2021-10-27
    • 2020-12-01
    • 2016-07-01
    相关资源
    最近更新 更多