实现Observable序列的更好方法

时间:2017-06-20 15:57:36

标签: angular rxjs observable

我一直在努力从我的http请求序列中获得正确的效果。我必须发出一个返回网站对象数组的初始请求。然后,我必须使用站点对象中找到的两个id来再发出两个http请求,以获取关联的站点组织名称和团队名称,并将它们添加到每个站点对象。所以我的步骤是:

  1. 获取网站列表
  2. 使用站点组织ID获取相关组织并将名称添加到站点对象。
  3. 使用站点团队ID获取相关团队并将团队名称添加到站点对象。
  4. 我的代码如下:

    private getSites() {
      this.spinner.show();
      this.sitesService.getSitesByUserId(this.userId)
        .subscribe(
          _data => {
            _data.map(site => {
              this.orgService.getOrganization(site['OrganizationId'])
                .map(org => {
                  site['orgName'] = org['org']['name'];
                  return org;
                })
                .flatMap(() => this.teamsService.getTeam(site['TeamId']))
                .map(team => {
                  site['teamName'] = team['team']['name'];
                  return team;
                })
                .subscribe(
                  _d => {
                    this.sites.push(site);
                  }
                );
            });
          },
          _error => {},
          () => {
            this.spinner.hide();
            this.loading = false;
          }
        );
    }
    

    在我的html中,我绑定了数据:

    <div *ngFor="let site of (sites | filterlist: value : 'name')">
      <card-directory-item (click)="siteSelected(site)">
        <i class="material-icons item-icon">location_city</i>
        <h2 class="item-name">{{ site.name }}</h2>
        <div class="item-field-1">{{ site.city }}  {{ site.state }} </div>
        <div class="item-static-field flex-column">
          <div>{{ site.teamName }}</div>
          <div>{{ site.orgName }}</div>
        </div>
      </card-directory-item>
    </div>
    

    问题是在完成所有步骤之前删除了微调器,并且每个列表项在完成的顺序中弹出到位。我知道为什么会这样。在移除微调器并显示数据之前,我只是无法弄清楚如何链接可观察流以完成所有内容。

    任何帮助都会很棒。感谢。

2 个答案:

答案 0 :(得分:2)

问题是你 python -c "import six; print (six.__version__)" 过早地“subscribe返回的观察者getSitesByUserId(this.userId)

这里你的代码看起来有点像这样

this.sitesService.getSitesByUserId(this.userId)
  .subscribe(
    otherAsyncOp,
    errorCallback,
    completeCallback
  )

问题是,只要onCompleteCallback返回的observable已经完成,就会在你的其他http请求完成之前调用getSitesByUserId

如果您想依次调用所有网址,则必须使用mergeMap(或flatMap这是别名)

this.sitesService.getSitesByUserId(this.userId)
  .mergeMap(sites => Rx.Observable.from(sites)) // create an observable per site
  .mergeMap(site => this.sitesService.getOrga(site.id)
                        .map(orga => Object.assign({}, site, { orgName: orga.org.name }))
  .mergeMap(site => this.sitesService.getTeam(site.teamId).map(team => Object.assign({}, site, {teamName: team.team.name})
  .reduce((sites, site) => sites.concat(site), [])
  .subscribe( /* there every request should be done and you can remove your loader */)

对不起,这是一个很长的答案,但你的问题非常广泛。我无法执行该代码,所以我不是100%确定它是否有效,但至少你明白这一点。

答案 1 :(得分:1)

您可以将每个网站映射到组织和团队请求的zip。 zip的第三个参数是一个可以用Object.assign组成最终对象的回调。

this.sitesService
  .getSitesByUserId(this.userId)
  .mergeMap(sites => Observable.from(sites))
  .mergeMap(site => {
    return Observable.zip(
      this.orgService.getOrganization(site['OrganizationId']),
      this.teamService.getTeam(site['TeamId']),
      (org, team) => Object.assign(site, {
        orgName: org['org']['name'],
        teamName: team['team']['name']
      })
    )
  })
  .subscribe(console.log)
相关问题