2

I want to have a condition inside flatMap that checks what was returned by first observable. If condition is not met I would like to break and navigate to different page.

this.service.getData(id)
  .flatMap((data) => {
    if (!data) {
      return Observable.throw(new NoDataFoundError());
    }
    return Observable.forkJoin(
      this.service.getData2(id2),
      this.service.getData3(id3),
    );
  })
  .subscribe(
    ([data2, data3]) => {
      this.data2= data2;
      this.data3= data3;
    },
    (err) => {
      if (err instanceof NoDataFoundError) {
        this.router.navigate(...);
      }
    }
  );   

Currently I'm throwing specific error and catching it however I don't like this solution as it's not the only piece of code that could throw an error and the if is not scaling.

I thought about filter or takeWhile operators but I won't be able to execute the redirect.

I also thought about returning Observable.of instead of throwing (in line 4) but then I would have to do the if in subscribe which also smells.

Mark van Straten
  • 9,287
  • 3
  • 38
  • 57
kit
  • 4,890
  • 3
  • 24
  • 23
  • The code looks pretty good as-is. Could you please elaborate on your concerns? There's a few different patterns that you could use, but it depends on what else is going on. – Richard Matsen Dec 02 '17 at 20:04

1 Answers1

9

You can execute this.router.navigate and return Observable.empty() inside .flatMap. This way you will have a single if statement.

.flatMap(data => {
  if (!data) {
    this.router.navigate(...);
    return Observable.empty();
  }
  return (...);
})

But usually Observables should be lazy and pure(free of side effects) this will make them predictable and easy to compose. Side effects should be performed only by subscriber.

In your specific case, it seems like the proper solution would be to put this logic in the route guard, like described here - https://stackoverflow.com/a/39162538/3772379

Oles Savluk
  • 4,315
  • 1
  • 26
  • 40
  • 2
    Thanks for the answer and sorry for late reply. I'm not sure about Observable.empty() as then I would have to IF it in subscribe. I get that Observables should be pure but I think my use case is quite common. I finally take inspiration from canActivate but changed it to [resolver](https://angular.io/guide/router#fetch-data-before-navigating). Thanks for help. – kit Dec 06 '17 at 09:26
  • No, that's the trick. When you return `.empty()` - nothing will be emitted which means you don't need to handle it in subscribe. – Oles Savluk Dec 06 '17 at 12:43
  • in my particular issue I can use canActivate/resolver which move fetching logic up in hierarchy. how can I proceed when I don't have any of mentioned mechanism. the only solution I can think of is using subscribe inside subscribe. – kit Dec 06 '17 at 13:20