-1

I am trying to make 2 HTTP requests and in the first call I try to create a record and then according to its results (response from the API method) I want to execute or omit the second call that updates another data. However, although I can catch the error in catchError block, I cannot get the response in the switchMap method of the first call. So, what is wrong with this implementation according to teh given scenario? And how can I get the response of the first result and continue or not to the second call according to this first response?

let result;
let statusCode;

this.demoService.create(...).pipe(
    catchError((err: any) => { ... }),
    switchMap(response => {

    // I am trying to get the response of first request at here
    statusCode = response.statusCode;

    if(...){
        return this.demoService.update(...).pipe(
            catchError((err: any) => { ... }),
            map(response => {
            return {
                result: response
              }
          }
        )
      )}
    }
  ))
  .subscribe(result => console.log(result));
Prag Dopravy
  • 201
  • 1
  • 3
  • 12
  • Can you try adding "tap(console.log)" on your pipe before the catchError just to confirm if the service's create returns a data and if it does log anything? tap is imported under rxjs/operators – KShewengger Nov 15 '20 at 07:05
  • What do you exactly mean by _"I cannot get the response in the `switchMap`"_? Are you not able to read the `statusCode` property? Is it throwing any error? Are you sure the property is available in the response? Please elaborate. – ruth Nov 15 '20 at 07:28
  • @MichaelD What I want to do is to get the response of the first call (create) and the according to its result, execute the second call (update) or break (if there is an error I think `switchMap` does not execute the others, but if there is no error I may want to break according to the response values). Of course I also need to get the response of the second call. – Prag Dopravy Nov 15 '20 at 07:39
  • @PragDopravy: And what exactly are you not able to do now? – ruth Nov 15 '20 at 07:42
  • @KShewengger I tried `tap(response => { console.log(response); debugger; }),`, but the code does not hit there and hit `switchMap` block (after that it still not hit tap() block). So, is there any misatake of my usage? – Prag Dopravy Nov 15 '20 at 07:46
  • @KShewengger Just a correction: When there is no error, it hits the `tap`, but the response is null even if there is a response from the first call. – Prag Dopravy Nov 15 '20 at 07:49
  • @MichaelD If there is error regarding to the first call, I can get it in the first `catchError` block. If there is no error, I cannot get the response (teher would be a status code, etc) in the `tap` block, etc. – Prag Dopravy Nov 15 '20 at 07:50
  • Hi @PragDopravy, tap is actually a logging platform in rxjs where you can debug things without affecting your response, so we tried to put console.log there if there's really anything that the service returns. If there isn't anything log inside that tap, it means that in the first place your "this.demoService.create(...)" didn't return anything to process through. Much better if you can check that instead, hope it helps – KShewengger Nov 15 '20 at 08:13
  • @KShewengger Thanks for help, yes it is good idea. On the other hand, can I use tap to check the result and then continue or not to execute the next call(update)? If so, how should I use it in or out of if blocks? – Prag Dopravy Nov 15 '20 at 08:30
  • @MichaelD Do you have any suggestion for this scenario? – Prag Dopravy Nov 15 '20 at 08:50
  • @PragDopravy, as for "check the result", yes but if it's something you'll use to not execute the next call, im afraid that's not possible. Instead how about using the rxjs iif? Gives you an if condition, that if this condition meets, you'll execute this call, if not, it will execute the other one. You can check the docs here: https://www.learnrxjs.io/learn-rxjs/operators/conditional/iif hope this helps :) – KShewengger Nov 15 '20 at 09:00
  • **Is [this](https://stackoverflow.com/questions/63929217/how-can-i-perform-a-series-of-http-calls-only-if-the-previous-is-successful-with/63960012#63960012) approach is the best option for sending multiple http requests and executing the next request based on a condition?** I am not sure if there is a more elegant solution using `switchMap` etc. Any suggestion would be appreciated. – Prag Dopravy Nov 15 '20 at 09:01
  • Your question is still isn't clear. One of the comment says _"hit `switchMap` block"_ and other comment says _"I cannot get the response"_. – ruth Nov 15 '20 at 09:02
  • Actually I was looking to `iif`, but I could not understand anything about the docs. Do you have any idea how to apply it to my scenario in my quesytion? Would you mind applying and posting code as asnwer? – Prag Dopravy Nov 15 '20 at 09:03
  • @MichaelD I just want to do that: At first, send first request and then get its result to an if condition. Then, based on this condition, I want to execute the second HTTP call or omit it and display message. So, how should I edit my code in order to use it with an elegant approach? – Prag Dopravy Nov 15 '20 at 09:05
  • Hi @PragDopravy, have posted an answer. hope it helps :) – KShewengger Nov 15 '20 at 09:11

2 Answers2

1

You can implement with iif

this.demoService
   .create(...)
   .pipe(
     // tap first to be sure there's actually a response to process through
     tap(console.log),

     // You can use any condition in your iif, "response.user.exists" is just a sample
     // If the condition is true, it will run the run the update$ observable
     // If not, it will run the default$
     // NOTE: All of them must be an observable since you are inside the switchMap
     switchMap(response => 
      iif(() => 
        response.user.exists, 
        this.demoService.update(response.id),    // Pass ID 
        of('Default Random Message')
      )
     ),
     catchError((err: any) => { ... })
   );

KShewengger
  • 7,853
  • 3
  • 24
  • 36
  • Thanks a lot for you help. I will check and inform you about the result several hours later. By the way, voted up for this good approach ;) – Prag Dopravy Nov 15 '20 at 09:29
  • Thanks a lot for you help. I will check and inform you about the result several hours later. By the way, voted up for this good approach ;) If there is a need, you can update your solution in the next hours that I will not be able to try. – Prag Dopravy Nov 15 '20 at 09:30
  • I just tried to use this approch. **1.** I need to pass `id` value to `update$` observable (`this.demoService.update(...)`). How can I pass that? **2.** How can I use this `iif` by defining the `update$` and `default$` variable contects (e.g. `this.demoService.update(...)`) inside that `iif` block? – Prag Dopravy Nov 15 '20 at 14:58
  • @PragDopravy Have updated my answer, you can actually still use the raw function without having to assign them in a variable and you can also pass data from there as well. Hope it helps :) – KShewengger Nov 16 '20 at 00:21
  • Thanks a lot for your lins helps. Actually the method is also an observable method and I am not sure if I should also define that methods with their pipe, map, switchMap, catchError block again? In that case I see that it would be tangled spaghetti :) So, could you please a method body and defiition place of `this.demoService.update(response.id)`? Sorry, but I am really new and I have been also tangled spaghetti for the last days with this problem. – Prag Dopravy Nov 16 '20 at 11:20
1

The question is still vague to me. I'll post a more generic answer to make few things clear

There are multiple things to note

  1. When an observable emits an error notification, the observable is considered closed (unless triggered again) and none of the following operators that depend on next notifications will be triggered. If you wish to catch the error notifications inside the switchMap, you could return a next notification from the catchError. Something like catchError(error => of(error)) using RxJS of function. The notification would then be caught by the following switchMap.

  2. You must return an observable from switchMap regardless of your condition. In this case if you do not wish to return anything when the condition fails, you could return RxJS NEVER. If you however wish to emit a message that could be caught by the subscriptions next callback, you could use RxJS of function. Replace return NEVER with return of('Some message that will be emitted to subscription's next callback');

import { of, NEVER } from 'rxjs';
import { switchMap, catchError, map } from 'rxjs/operators';

this.demoService.create(...).pipe(
  catchError((err: any) => { ... }),
  switchMap(response => {
    statusCode = response.statusCode;

    if (someCondition) {
      return this.demoService.update(...).pipe(  // emit `update()` when `someCondition` passes
        catchError((err: any) => { ... }),
        map(response => ({ result: response }))
      );
    }
    // Show error message
    return NEVER;               // never emit when `someCondition` fails
  }
)).subscribe({
  next: result => console.log(result),
  error: error => console.log(error)
});
ruth
  • 29,535
  • 4
  • 30
  • 57
  • Thanks a lot for you help. I will check and inform you about the result several hours later. By the way, voted up for this good approach ;) If there is a need, you can update your solution in the next hours that I will not be able to try. – Prag Dopravy Nov 15 '20 at 09:30
  • I tried this approach and see that I need to treat the response in the first `catchError()` block. And then I return the `err` in that block but I cannot get it in the `switchMap` as `response` variable. So, should I get the value returned from `catchError` in the `switchMap` block? – Prag Dopravy Nov 15 '20 at 14:33
  • The solution to that question is already addressed in the answer point 1. – ruth Nov 15 '20 at 15:31
  • Can I alsoı apply some logic in the `catchError` block also? Because I only catch some response in that block but I am not sure if it is the correct place to handle such logic. Any idea? – Prag Dopravy Nov 17 '20 at 13:39
  • You could handle logic in the `catchError` block. But it depends on the logic. – ruth Nov 17 '20 at 13:54