0
votes

I need to chain many http requests, the number of request is variable and they are not dependant from the result of the previous one, I just need to keep the returned object of the last request.

I've been given two solutions on this thread, the first solution using Observable.concat() works, but a contributor suggested me a more elegant way to perform that, by using concatMap().

Here's my current code :

MyComponent

submitForms() {
    var formToSubmit = [...]; // Contains dirty forms that be sent to the backend (they are simple Angular2 Components)
    var id = '123';

    Rx.Observable.from(formToSubmit)
             .concatMap(
                  (form) => {
                      return this.formPartService.submitFormPart(id, form.getContext(), form.getValues());
                  }
              )
              .combineAll()
              .subscribe( (val) => {
                  console.log(val); // Should return an array of each value returned. I'll keep the last one. 
              } );
}

As you can see, I have an array with all dirty forms that need to be submitted to the backend. I create an Observable from this array, then I use concatMap() to subscribe to the Observable of the passed function and wait for the result, before firing the next subscription.

Here's the submitFormPart() method from my FormPartService

submitFormPart(id: string, part: string, data: any) {

    let body = JSON.stringify({
        id: id,
        part: part,
        data: data
    });

    return this.http.post('/form-part', body)
                    .map( (res) => { 
                            let body = res.json();
                            if (body.data){
                                return body.data;
                            }
                        } 
                    );
}

It should simply return a simple cold Observable. Everything should work fine...

When I submit my forms, I have the error unknown type returned, the complete stacktrace :

core.umd.js?e2a5:3462 EXCEPTION: unknown type returnedErrorHandler.handleError @ core.umd.js?e2a5:3462next @ core.umd.js?e2a5:6924schedulerFn @ core.umd.js?e2a5:6172SafeSubscriber.__tryOrUnsub @ VM1077851:223SafeSubscriber.next @ VM1077851:172Subscriber._next @ VM1077851:125Subscriber.next @ VM1077851:89Subject.next @ VM1077847:55EventEmitter.emit @ core.umd.js?e2a5:6164onError @ core.umd.js?e2a5:6388onHandleError @ core.umd.js?e2a5:6263ZoneDelegate.handleError @ zone.js?fad3:207Zone.runTask @ zone.js?fad3:139ZoneTask.invoke @ zone.js?fad3:304
core.umd.js?e2a5:3467 ORIGINAL STACKTRACE:ErrorHandler.handleError @ core.umd.js?e2a5:3467next @ core.umd.js?e2a5:6924schedulerFn @ core.umd.js?e2a5:6172SafeSubscriber.__tryOrUnsub @ VM1077851:223SafeSubscriber.next @ VM1077851:172Subscriber._next @ VM1077851:125Subscriber.next @ VM1077851:89Subject.next @ VM1077847:55EventEmitter.emit @ core.umd.js?e2a5:6164onError @ core.umd.js?e2a5:6388onHandleError @ core.umd.js?e2a5:6263ZoneDelegate.handleError @ zone.js?fad3:207Zone.runTask @ zone.js?fad3:139ZoneTask.invoke @ zone.js?fad3:304
core.umd.js?e2a5:3468 TypeError: unknown type returned
    at Object.subscribeToResult (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:297:2), <anonymous>:69:27)
    at CombineLatestSubscriber._complete (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:797:2), <anonymous>:108:46)
    at CombineLatestSubscriber.Subscriber.complete (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:131:2), <anonymous>:114:18)
    at MergeMapSubscriber.notifyComplete (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:430:2), <anonymous>:155:30)
    at InnerSubscriber._complete (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:315:2), <anonymous>:30:21)
    at InnerSubscriber.Subscriber.complete (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:131:2), <anonymous>:114:18)
    at MapSubscriber.Subscriber._complete (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:131:2), <anonymous>:132:26)
    at MapSubscriber.Subscriber.complete (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:131:2), <anonymous>:114:18)
    at XMLHttpRequest.onLoad (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:261:2), <anonymous>:1499:42)
    at ZoneDelegate.invokeTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:236:37)
    at Object.onInvokeTask (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:101:2), <anonymous>:6233:41)
    at ZoneDelegate.invokeTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:235:42)
    at Zone.runTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:136:47)
    at XMLHttpRequest.ZoneTask.invoke (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:304:33)
  -------------   Elapsed: 336 ms; At: Mon Nov 14 2016 13:58:04 GMT+0100 (Paris, Madrid)   -------------  
    at Object.onScheduleTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2666:2), <anonymous>:76:18)
    at ZoneDelegate.scheduleTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:213:49)
    at Zone.scheduleEventTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:156:39)
    at zoneAwareAddListener (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:773:14)
    at XMLHttpRequest.addEventListener (eval at createNamedFn (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2)), <anonymous>:3:43)
    at Observable.eval [as _subscribe] (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:261:2), <anonymous>:1540:22)
    at Observable.subscribe (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:113:2), <anonymous>:56:27)
    at Observable._subscribe (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:113:2), <anonymous>:114:28)
    at MapOperator.call (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:273:2), <anonymous>:54:23)
    at Observable.subscribe (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:113:2), <anonymous>:53:22)
    at Object.subscribeToResult (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:297:2), <anonymous>:21:27)
    at MergeMapSubscriber._innerSub (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:430:2), <anonymous>:120:38)
    at MergeMapSubscriber._tryNext (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:430:2), <anonymous>:117:14)
    at MergeMapSubscriber._next (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:430:2), <anonymous>:100:18)
    at MergeMapSubscriber.notifyComplete (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:430:2), <anonymous>:152:18)
    at InnerSubscriber._complete (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:315:2), <anonymous>:30:21)
    at InnerSubscriber.Subscriber.complete (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:131:2), <anonymous>:114:18)
    at MapSubscriber.Subscriber._complete (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:131:2), <anonymous>:132:26)
    at MapSubscriber.Subscriber.complete (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:131:2), <anonymous>:114:18)
    at XMLHttpRequest.onLoad (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:261:2), <anonymous>:1499:42)
    at ZoneDelegate.invokeTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:236:37)
    at Object.onInvokeTask (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:101:2), <anonymous>:6233:41)
    at ZoneDelegate.invokeTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:235:42)
    at Zone.runTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:136:47)
    at XMLHttpRequest.ZoneTask.invoke (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:304:33)
  -------------   Elapsed: 861 ms; At: Mon Nov 14 2016 13:58:03 GMT+0100 (Paris, Madrid)   -------------  
    at Object.onScheduleTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2666:2), <anonymous>:76:18)
    at ZoneDelegate.scheduleTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:213:49)
    at Zone.scheduleEventTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:156:39)
    at zoneAwareAddListener (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:773:14)
    at XMLHttpRequest.addEventListener (eval at createNamedFn (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2)), <anonymous>:3:43)
    at Observable.eval [as _subscribe] (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:261:2), <anonymous>:1540:22)
    at Observable.subscribe (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:113:2), <anonymous>:56:27)
    at Observable._subscribe (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:113:2), <anonymous>:114:28)
    at MapOperator.call (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:273:2), <anonymous>:54:23)
    at Observable.subscribe (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:113:2), <anonymous>:53:22)
    at Object.subscribeToResult (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:297:2), <anonymous>:21:27)
    at MergeMapSubscriber._innerSub (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:430:2), <anonymous>:120:38)
    at MergeMapSubscriber._tryNext (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:430:2), <anonymous>:117:14)
    at MergeMapSubscriber._next (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:430:2), <anonymous>:100:18)
    at MergeMapSubscriber.Subscriber.next (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:131:2), <anonymous>:89:18)
    at ArrayObservable._subscribe (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:370:2), <anonymous>:114:28)
    at ArrayObservable.Observable.subscribe (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:113:2), <anonymous>:56:27)
    at Observable._subscribe (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:113:2), <anonymous>:114:28)
    at MergeMapOperator.call (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:430:2), <anonymous>:75:23)
    at Observable.subscribe (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:113:2), <anonymous>:53:22)
    at Observable._subscribe (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:113:2), <anonymous>:114:28)
    at CombineLatestOperator.call (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:797:2), <anonymous>:74:23)
    at Observable.subscribe (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:113:2), <anonymous>:53:22)
    at EditorComponent.submitAll (eval at <anonymous> (http://192.168.33.10:7777/app.js:1220:2), <anonymous>:81:18)
    at _View_EditorComponent0._handle_click_52_0 (EditorComponent.ngfactory.js:602:28)
    at eval (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:101:2), <anonymous>:9698:28)
    at eval (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:209:2), <anonymous>:1877:40)
    at eval (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:209:2), <anonymous>:1990:115)
    at ZoneDelegate.invoke (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:203:28)
    at Object.onInvoke (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:101:2), <anonymous>:6242:41)
    at ZoneDelegate.invoke (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:202:34)
    at Zone.runGuarded (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:110:47)
    at NgZoneImpl.runInnerGuarded (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:101:2), <anonymous>:6271:82)
    at NgZone.runGuarded (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:101:2), <anonymous>:6504:77)
    at HTMLButtonElement.outsideHandler (eval at <anonymous> (http://192.168.33.10:7777/vendor.js:209:2), <anonymous>:1990:83)
    at ZoneDelegate.invokeTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:236:37)
    at Zone.runTask (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:136:47)
    at HTMLButtonElement.ZoneTask.invoke (eval at <anonymous> (http://192.168.33.10:7777/polyfills.js:2660:2), <anonymous>:304:33)ErrorHandler.handleError @ core.umd.js?e2a5:3468next @ core.umd.js?e2a5:6924schedulerFn @ core.umd.js?e2a5:6172SafeSubscriber.__tryOrUnsub @ VM1077851:223SafeSubscriber.next @ VM1077851:172Subscriber._next @ VM1077851:125Subscriber.next @ VM1077851:89Subject.next @ VM1077847:55EventEmitter.emit @ core.umd.js?e2a5:6164onError @ core.umd.js?e2a5:6388onHandleError @ core.umd.js?e2a5:6263ZoneDelegate.handleError @ zone.js?fad3:207Zone.runTask @ zone.js?fad3:139ZoneTask.invoke @ zone.js?fad3:304
VM1077851:227 Uncaught TypeError: unknown type returned(…)

I can't find where this error comes from. I tried the following, to test from the raw example I've been provided, and to debug in a certain way :

MyComponent

submitForms() {
    ...
    Rx.Observable.from(formToSubmit)
                 .concatMap(
                     (form) => {
                         return Rx.Observable.of(this.formPartService.submitFormPart(id, form.getPartName(), form.getValues());
                     }
...
}

Returning an Observable of an Observable... like this, the error disappear, the http calls are well executed, but they are executed at the same time in parallel. That's not what I want, and that's certainly not viable of course, but it may just show that the problem comes from the return of my submitFormPart(), and so the return of Http.post().map() from Angular2 that concatMap() doesn't appreciate...

If it can be useful, here are my current versions :

"@angular/common": "2.0.0",
"@angular/compiler": "2.0.0",
"@angular/core": "2.0.0",
"@angular/forms": "2.0.0",
"@angular/http": "2.0.0",
"@angular/material": "^2.0.0-alpha.9-3",
"@angular/platform-browser": "2.0.0",
"@angular/platform-browser-dynamic": "2.0.0",
"@angular/router": "3.0.0",
"rxjs": "5.0.0-beta.12"

I didn't try to upgrade these yet...

Thanks for your help

2
Can you strip this only to necessary minimum? I have no idea where's the error thrown. Maybe this Rx.Observable.from(formToSubmit)? What's formToSubmit?martin
Hi, I did my best to strip the elements to the minimum. formToSubmit is a simple array, containing @ViewChild variables pointing to subcomponents that encapsulate angular forms. Like I said above, I use this array because the number of form to submit is variable and depends if they are dirty or not. I create an Observable from this array to process these forms one by one, like a contributor suggested me to doDlafCreative

2 Answers

0
votes

I just need to keep the returned object of the last request.

Use .takeLast(n) to get the latest n observable, for your case, try changing your code to

Rx.Observable.from(formToSubmit)
   .concatMap(
       (form) => {
          return this.formPartService.submitFormPart(id, form.getContext(), form.getValues());
       }
   )
   .takeLast(1) // get only the latest observable 
   .subscribe( (val) => {
      console.log(val); // Now this should return the last emitted value
   });

Example Plunker

0
votes

You can do the following:

Rx.Observable.zip(
  formToSubmit.map(form => this.formPartService.submitFormPart(id, form.getContext(), form.getValues()))
).subscribe(results => {
  let firstFormResult = results[0];
  let secondFormResult = result[1];
  ... etc. etc.
});

I do this whereever I have multiple independent requests... as soon as ALL requests are completed, Observable.zip will invoke your subscription.