【问题标题】:identify last object within mergeMap识别 mergeMap 中的最后一个对象
【发布时间】:2019-01-06 13:02:57
【问题描述】:

我想将我的排名实体与我在 ionic4/angular7 中的成员实体合并。

我实际做的是:

this.rankingObs = this.rankingService.getAll().pipe(
  mergeMap(ranking => { 
    ranking.forEach(rank => {
      this.membersService.get(rank.key).subscribe(member => {
        rank.firstName = member.firstName;
        rank.lastName = member.lastName;
        rank.keyName = member.key;
      })
    });
  return ranking;
  }),
).subscribe((merged) => {
  let ranking = merged as RankingModelAll;
  this.rankingmodel.push(ranking);

   //***
   //CAN'T BE CORRECT HERE :( i only want to sort at last and also set the preloadingDone at Last
  this.rankingmodel = this.rankingmodel.sort((n1,n2) => {
    if(n1.points < n2.points) {
      return 1;
    }
    if(n1.points > n2.points) {
      return -1;
    }
    return 0;
  });
  this.preloadingDone = true;
  // ****
});

将我的成员姓名合并到排名模型中,但是我如何识别我的合并实体的最后一次运行以将我的 preloadingDone 标志设置为 true 并进行最高排名的排序?

【问题讨论】:

    标签: angular ionic-framework rxjs angularfire


    【解决方案1】:

    我对你的代码没有多少意见。

    1. 嵌套订阅不是很好的做法。你应该把它弄平。
    2. 使用subscribe(next, error, complete)complete 处理程序将您的标志 preloadingDone 设置为 done
    3. 在您的情况下,我将使用插入排序数组,而不是在最后对整个数组进行排序。

    看看下面的sn-p:

        const orderedInsert = compareFn => (array, item) => {/** need to code this :( */};
    
    const insetRank = orderedInsert((n1,n2) => {
            // your compare fn
            if(n1.points < n2.points) {
              return 1;
            }
            if(n1.points > n2.points) {
              return -1;
            }
            return 0;
          }
    );
    
    this.rankingObs = this.rankingService.getAll().pipe(
      mergeMap(ranking => from(ranking))
      mergeMap(rank =>this.membersService.get(rank.key).pipe(map(member => ({
          ...rank,
          ...member 
      }))
    ).subscribe((rankMerged:RankingModelAll) => 
      insetRank(this.rankingmodel, rankMerged), 
      e => console.log(e), 
      () => {
        this.preloadingDone = true;
        console.log('Done')
     });
    

    【讨论】:

    • 感谢您的提示,这是非常酷的示例。但还有一个问题,我如何才能将其展平而不是加入数据?如果我将名称放入排名集合中,如果用户更改他的姓名,则将使用旧数据:(
    • 还有一个问题,管道中有第二个 mergeMap,我不明白这是什么,我的 vscode 说这不起作用:(
    • @HaraldWiesinger,第二个合并运算符是扩展运算符mzl.la/2GbFCdo。在这种情况下,它将 2 个对象合并为 1 个。(类似于对象分配)。您的 vscode 抱怨,因为我的代码缺少 2 个右括号。 ...member }))))
    • but one more question, how can I flatten this instead of joining the data? - 我没有。真的明白你的意思,你能详细说明一下吗?
    • 感谢您的回答!你告诉我在这种情况下嵌套不是一个好习惯。所以实际上我有一个我的排名节点,其中用户的 id 所在的位置。所以我认为扁平化的意思是,不是将用户 ID 放在那里,而是希望我将名字和姓氏放在那里,或者我错了? ;)
    【解决方案2】:

    merge map 中的返回值应该是一个 observable。在上面的代码中,排名将在订阅完成之前返回,因此它将使用尚未更新的排名数据。

    我想你想要的可能是这样的:

    this.rankingObs = this.rankingService.getAll().pipe(
      mergeMap(ranking => {
        // an array of all observables which will return the merged rank and member data
        const ranksWithMembers = ranking.map(rank=>{
            return this.memberService.get(rank.key).pipe(
                map(member=>{
                    return {
                        ...rank, 
                        firstName: member.firstName, 
                        lastName: member.lastName,
                        keyName: member.key,
                    }
                })
            )
        })
        // does not emit until all observables in array emit. Emits all results.
        return forkJoin(ranksWithMembers)
      }),
    ).subscribe(...)
    

    【讨论】:

    • 感谢您的解决方案,我试过了,但似乎发出不工作:(订阅从未到达
    • 尝试将tap(member=&gt;console.log('member', member)) 放在map 之前,看看是否能达到那么远
    • 会不会因为firebase的realmtimedatabase而没有发出?
    • 将错误处理程序添加到订阅,可能是某处的类型或服务器响应错误。那么它将只订阅错误处理程序。
    猜你喜欢
    • 2011-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多