【问题标题】:Wait for Observable to complete in order to submit a form等待 Observable 完成以提交表单
【发布时间】:2017-05-14 21:14:05
【问题描述】:

我有一个“新旅行”表单,用户可以在其中写下参与者的姓名,然后提交表单以创建旅行。 提交时,我使用名称查询 Firebase 数据库,以获取参与者 (/users) 的 ID。然后,我将 ID 添加到行程对象的 participantsID 字段,然后将新行程推送到 Firebase。

问题是 Firebase 查询是异步的并返回一个 Observable,因此我的函数将在 Observable 完成之前继续推送对象,因此新对象的参与者ID 字段为空。

是否有任何方法可以等待 observable 完成(以一种同步方式)以便我可以操作数据然后继续?到目前为止,我所有解决此问题的尝试都失败了。

这是我的简单代码。

getUserByAttribute(attribute, value) {
  return this.db.list('/users', {
    query: {
      orderByChild: attribute,
      equalTo: value,
      limitToFirst: 1
    }
  });
}

createTrip(trip) {
  for(let name in participantsName.split(',')) {
    getUserByAttribute('username', name)
      .subscribe( user => trip.participantsID.push(user[0].$key) );
  }

  this.db.list('/trips').push(trip);
}

【问题讨论】:

    标签: javascript angular firebase observable angularfire2


    【解决方案1】:

    您可以通过 forkJoin 将所有 Observable 处理成一个 Observable

    createTrip(trip) {
      var observableArray: any = participantsName.split(',')
        .switchMap((name)=> getUserByAttribute('username', name))
      Observable.forkJoin(observableArray).subscribe(
        trips => trips.forEach((trip) => {
           this.db.list('/trips').push(trip);
        })
      );
    } 
    

    【讨论】:

    • 问题是我需要将所有用户 ID 添加到我的 SINGLE trip 对象中,然后再将其推送到数据库。我可以将它们添加到订阅函数中,但是在将每个 Id 添加到对象之后,如何将 trip 对象推送到数据库?
    • 我也收到错误participantsName.split(...).switchMap is not a function
    【解决方案2】:

    最后我使用了@Pankaj Parkar 的部分答案来解决问题。 我forkJoin 映射拆分后的名称返回的所有 Observable,我订阅了该 Observable,其结果包含一个数组数组,其中内部数组包含一个用户对象。

    getUserByAttribute(attribute, value) {
      return this.db.list('/users', {
        query: {
          orderByChild: attribute,
          equalTo: value,
          limitToFirst: 1
        }
      }).first();
    }
    
    createTrip(trip) {
      Observable.forkJoin(
        trip.participantsName.split(',')
          .map(name => getUserByAttribute('name', name))
        ).subscribe(
          participants => {
            trip.participants = participants.map( p => p[0].$key);
            this.tripService.createTrip(trip);
          }
        );
      }   
    }
    

    【讨论】:

    • 很高兴知道我的回答有帮助。我使用了switchMap,因为我认为getAttribute 会返回承诺。
    【解决方案3】:

    你有一个难题。在推送新行程之前,您必须获取用户信息。

    由于内存泄漏问题,您不能每次都进行新订阅(或者要小心取消订阅)。如果您使用的是 Firebase,则可以使用 AngularFire subject 支持。

    您可以通过在查询中使用主题(使用等于)来更新订阅,然后使用 .next(user) 推送用户进行检索。

    那么你仍然需要等待所有用户。为此,您可以只拥有一个订阅并同步获取所有 ID,或者拥有多个订阅以更快地获得多个结果(但这很困难)。

    为了解决这个问题,我创建了:

    • 回调队列(只是数组,但使用 push() 和 unshift() 方法)

    • 值队列

    • 一个订阅一个主题。

    如果你想要一个 ID,你必须:

    • 推送价值
    • 推送将检索返回值的回调。

    您应该使用函数来推送,因为如果堆栈中没有值(开始!),您将不得不调用 .next()。 在您的订阅中,在其回调中,即当您收到远程用户对象时,您可以调用堆栈中的第一个回调。不要忘记弹出堆栈的值和回调并调用 next() 以获得下一个值(如果有的话)。

    这样,您可以在最后一个用户的最后一个回调中推送您的行程。而且都是回调,意味着你的应用没有被中断。

    我还没有决定我们是否应该在云功能中这样做。因为用户必须保持连接,这使用他的数据/处理器。但是最好将所有代码放在同一个地方,并且云功能对于 Firebase 的免费版本是有限的。 Firebase 开发人员有什么建议?

    我进行了很多搜索以找到更好的解决方案,所以如果您有的话,请分享。我认为这有点复杂,但它工作得很好。当用户想要添加新航班时,我遇到了同样的问题,我需要先获取机场信息(坐标)并推送多个对象(详细信息、地图等)

    【讨论】:

    • 有很多东西,你能给我举个例子吗?我觉得我想完成一件简单的事情,比如在发送表单之前获取一些数据,但这比我想象的要复杂得多
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-06
    • 2018-08-06
    • 1970-01-01
    • 2017-09-20
    • 1970-01-01
    相关资源
    最近更新 更多