2017-06-20 3 views
0

Ich habe gekämpft, um den richtigen Effekt von meiner Sequenz von HTTP-Anfragen zu bekommen. Ich muss eine erste Anfrage machen, die ein Array von Site-Objekten zurückgibt. Dann muss ich zwei weitere HTTP-Anfragen mit zwei IDs machen, die im Site-Objekt gefunden wurden, um den Namen der assoziierten Sites und den Team-Namen zu erhalten und sie zu jedem Site-Objekt hinzuzufügen. Also meine Schritte sind:Bessere Möglichkeit, beobachtbare Sequenz zu implementieren

  1. Get Liste von Websites
  2. Nutzung Baustellenorganisation id ähnliche Organisation zu erhalten und den Namen auf die Website-Objekt hinzufügen.
  3. Verwenden Sie die Site-Team-ID, um das zugehörige Team abzurufen und den Team-Namen zum Site-Objekt hinzuzufügen.

Mein Code ist wie folgt:

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

In meinem html ich die Daten am Bindung:

<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> 

Das Problem ist der Spinner entfernt wird, bevor alle Schritte abgeschlossen sind und Jedes Listenelement erscheint in der Reihenfolge, in der es fertig ist. Ich weiß, warum das passiert. Ich war einfach nicht in der Lage herauszufinden, wie man den beobachtbaren Stream verkettet, um alles zu beenden, bevor der Spinner entfernt und Daten angezeigt werden.

Jede Hilfe wäre großartig. Vielen Dank.

Antwort

1

Das Problem ist, dass Sie subscribe zu der beobachtbaren von getSitesByUserId(this.userId) zurückgegeben "zu früh".

Hier Ihr Code sieht ein bisschen wie diese

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

Die Sache ist, wird die onCompleteCallback aufgerufen wird, wenn die beobachtbaren zurück von getSitesByUserId abgeschlossen haben, das ist, bevor Sie andere HTTP-Anfragen abgeschlossen haben.

Wenn Sie alle die URL, um sequentiell wollen rufen, müssen Sie mergeMap verwenden (oder flatMap, die ein Alias ​​ist) auf diese Weise

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 */) 

Leider ist dies eine ziemlich lange Antwort, aber Ihre Frage ist sehr breit . Ich konnte diesen Code nicht ausführen, also bin ich mir nicht 100% ig sicher, dass es funktioniert, aber zumindest bekommst du die Idee.

+0

Vielen Dank. Das hat sehr gut funktioniert und ich habe viel von deiner Lösung gelernt. Eigentlich von beiden gelernt. :) – Aaron

+0

Super, danke für dein Feedback. Lernen ist das beste Gefühl aller Zeiten :) Ich bin froh, dass ich helfen konnte – atomrc

1

Sie können jede Site einer Zip der Org- und Teamanforderungen zuordnen. Das dritte Argument von zip ist ein Rückruf, der das endgültige Objekt mit Object.assign zusammensetzen kann.

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) 
+0

Das ist schöner Code, aber es funktioniert nicht in meinem Fall. Ich glaube, das würde funktionieren, wenn Site in MergeMap ein einzelnes Site-Objekt wäre, aber in meinem Fall ist es ein Array von Site-Objekten. Wenn der Code den Aufruf für this.teamService.getTeam (site ['TeamId']) ausführt, ist die ID nicht definiert. – Aaron

+0

Ich sehe. Du brauchst also nur noch eine 'mergeMap'. Code korrigiert. –

Verwandte Themen