2017-08-21 3 views
3

Ich bin neu in Angular 4, also akzeptiere ich, dass ich besser meinen Ansatz ändern kann.Warten auf ein Abonnement und ein Versprechen in Angular 4

Was ich habe, ist dies:

ngOnInit() { 
    this.route.paramMap 
     .switchMap((params: ParamMap) => { 
      var id = +params.get('id'); 
      return this.service.getOne(id); 
     }).subscribe(data => { 
      if (data.clientID === 0) { 
       data.clientID = +this.route.snapshot.queryParams["parentId"]; 
      } 
      this.model = data; 
     },); // Subscription 

    this.lookupService.getList("clients").then(data => { 
     this.clients = data; 
    }); // Promise 
} 

Auf der Client-Seite, ich brauche einen anderen Server Anruf zu machen, aber nur, wenn ich Daten aus den beiden obigen Anrufe. Mit anderen Worten, ich brauche die Subscription zu vervollständigen (gut, nicht abgeschlossen, aber den Wert) und die Promise abzuschließen.

Gibt es so etwas wie eine Promise.all, die funktionieren würde, um auch sicherzustellen, this.model wurde in den Subscription Aufruf eingestellt?

EDIT: Ich könnte diese in Serie ausführen, aber aus Leistungsgründen möchte ich sie parallel ausführen.

BEARBEITEN 2: Die Promise war zum Laden von Suchdaten zum Auffüllen eines Dropdown-Menüs nur einmal vorgesehen. Die Subscription sollte das Hauptmodell bei jeder Änderung von "ID" in der URL ändern.

+0

So 'lookupService.getList()' sollte nur einmal ausgeführt werden. 'service.getOne()' sollte jedes Mal ausgeführt werden, wenn ein neuer Parameter richtig ankommt? Ok, was ist mit dem 3. Anruf?Du hast gesagt, es sollte warten, bis die anderen beiden produzieren, aber was ist mit der ersten Produktion? Sollte der 3. Anruf nur einmal oder jedes Mal, wenn ein neuer Parameter ankommt, erfolgen? – BeetleJuice

+0

@BeetleJuice Ich habe den dritten Aufruf vereinfacht, nur eine lokale Filterfunktion auszuführen, aber es muss mit jedem neuen Parameter ausgeführt werden, nachdem 'service.getOne()' aktualisiert wurde. –

+0

Ich habe meine Antwort umgeschrieben und eine Demo zur Verfügung gestellt. Wenn Sie sich für eine bessere Lösung entscheiden, bitte unterrichten Sie uns! – BeetleJuice

Antwort

2

tun Observable.forkJoin verwenden können, die Promise.all entspricht.

Observable.forkJoin(this.route.paramMap 
     .switchMap((params: ParamMap) => { 
      var id = +params.get('id'); 
      return this.service.getOne(id); 
     }), Observable.fromPromise(this.lookupService.getList("clients"))) 
     .subscribe((arrayOfResults) => { 
      [firstCallResult, secondCallResult] = arrayOfResults; 
      this.model = firstCallResult; 
     }); 

EDIT:

den Teil über die Aktualisierung Parameter zu erfüllen, können wir den Observable.combineLatest Operator verwenden:

About Observable.combineLatest

Da es immer dann, wenn einer der Quelle emittiert Observablen emittiert, Der Subskriptionsblock wird ausgeführt. Da das andere Observable aus einem Versprechen stammt, wird es vervollständigt und nicht mehr Werte ausgeben, so dass this.lookupService.getList() nicht erneut aufgerufen wird.

Observable.combineLatest(this.route.paramMap 
      .switchMap((params: ParamMap) => { 
       var id = +params.get('id'); 
       return this.service.getOne(id); 
      }), Observable.fromPromise(this.lookupService.getList("clients"))) 
      .subscribe((arrayOfResults) => { 
       [firstCallResult, secondCallResult] = arrayOfResults; 
       this.model = firstCallResult; 
       // do whatever else is needed 
      }); 
+1

Unsere "Service-Schicht" hat Dutzende von Objekten, die Promises zurückgeben (wie im equinary.io-Tutorial) und Änderungen, die globale Auswirkungen haben. Wenn dies jedoch der einzige Weg ist, dann nehme ich an, dass ich es tun muss. –

+1

@KevinWelsh sehe meine Bearbeitung. Sie können 'Observable.fromPromise()' verwenden, um ein Versprechen in ein Observable zu konvertieren. – Matsura

+0

Schön. 'combineLatest()' ist der richtige Operator dafür denke ich, – BeetleJuice

0

Sie können Sie so etwas wie

ngOnInit() { 
    this.lookupService.getList("clients").then(data => { 
    this.clients = data; 
    this.route.paramMap 
    .switchMap((params: ParamMap) => { 
     var id = +params.get('id'); 
     return this.service.getOne(id); 
    }).subscribe(data => { 
     if (data.clientID === 0) { 
      data.clientID = +this.route.snapshot.queryParams["parentId"]; 
     } 
     this.model = data; 
==>Call ur method here!!! 
    },);/

}); // Promise 
+0

Ich werde die Frage aktualisieren, aber ich möchte diese Serveraufrufe nicht in Serie ausführen. –

3
  1. Verwenden .zip() auf das Versprechen und das Modell-Abruf beobachtbar. Es wird ein Array von Ergebnissen ausgegeben, mit denen Sie lokale Klasseneigenschaften auffüllen können. An dieser Stelle können Sie Ihre 3. Funktion aufrufen (den lokalen Filter). Sobald das Versprechen abgeschlossen ist, wird auch .zip, so dass diese Kette nur einen Ausgang hat.

  2. Sobald .zip() abgeschlossen ist, hören Sie neue Route Parameter und holen Sie entsprechende Modelle. Wenn sie eintreffen, legen Sie Ihre lokale Klasseneigenschaft fest und führen Sie Ihren lokalen Filter aus

  3. Abonnieren Sie die gesamte Kette auszulösen.

    ngOnInit(){ 
        // Will run getOne() with every new parameter 
        let getModelForParam = this.route.paramMap.switchMap(
         params => this.service.getOne(+params.get('id')) 
        ).share(); 
    
        // Zip will wait for both below to produce, then output an array of results 
        // and complete (since getList() promise completes after one call) 
        Observable.zip(
         this.lookupService.getList('clients'), 
         getModelForParam 
        ).do(results => { // capture results in order of .zip() arguments 
         this.clients = results[0]; 
         this.model = results[1]; 
         this.localFilter();// call the filter 
        }).concat(// this part executes once .zip() completes 
         getModelForParam // continue fetching models for new params 
        ).subscribe(model => { 
         this.model = model; 
         this.localFilter(); 
        }) 
    } 
    

Live demo

+1

Ich glaube, 'switchMap' wird nicht abgeschlossen, da die Komponente wiederverwendet werden kann, und Sie' take (1) 'nicht verwenden können, da Sie alle folgenden Updates zu Params verlieren, wenn die Komponente wieder verwendet wird –

+1

@MaximKoretskyi Interessantes Problem. Ich denke, du hast recht. Da dies wahrscheinlich jedes Mal ausgeführt werden soll, wenn ein neuer Parameter ausgegeben wird, sollten wir nicht nach 1 Ergebnis anhalten. Ein besserer Ansatz ist die Verwendung von 'zip'. Es wird auch die Anrufe parallel machen, aber nur einmal senden, wenn beide Antworten ankommen. Ich werde meine Antwort aktualisieren. – BeetleJuice

+0

@MaximKoretskyi eigentlich jetzt, dass ich darüber nachdenke, hat das OP eines der Ergebnisse kommen als eine Versprechen Auflösung, die one-and-done bedeutet. Wenn also der dritte Aufruf nach den Ergebnissen der ersten zwei erfolgt, wird der dritte Aufruf nur einmal ausgeführt (weil ein Versprechen nur einmal aufgelöst wird). Daher sehe ich in der Praxis nicht den Unterschied zwischen 'forkJoin/take (1)' und 'zip' – BeetleJuice

Verwandte Themen