How to combine 2 Observable in angular app

67 Views Asked by At

I have a problem, how to combine 2 observable in a complicated case. I show you the code as follow:

so I have 2 Observables: This 2 Observable will be used in canActivate(): Observable<boolean| UrlTree>

1: this.loadingState : Observable<LoadingState>
2: currentData : Observable<currentData>

so loadingState will return the state of loaded data, either everything ok, or error. in my case I will check firstly the loadingState, if everything is ok, if not, will return to error page. If everything is ok, then I will pipe to currentData and do some check in currentData.

my first code as follow, but it is not working

return this.loadingState.pipe(
 map((loadingState) => {       // operator function 1
   if(loadingState.error){
      // then return something.
   }
}),

 () => {                      // operator function 2
  this.currentData.pipe(
   map((currentData) => {
     // here will check current, also return something.
   })

  );
}

);

this first problem is: the first map loadingState is not all code path return a value. my idea is, if loadingState.error is true, the oerator function 2 will not carried out.

I have googled also some solution such as with combineLatest or forkjson, It could also not work.

maybe you have some solution

best thx

2

There are 2 best solutions below

2
Joosep Parts On

We could use switchMap to switch to the other observable when error is false.

import { Component, OnDestroy, OnInit } from '@angular/core';
import { map, Observable, of, Subject, switchMap, takeUntil } from 'rxjs';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit, OnDestroy {
  loadingState$: Observable<any> = of({ error: false });
  currentData$: Observable<any> = of({ data: 'data' });
  destroyed$: Subject<void> = new Subject();

  ngOnInit() {
    this.loadingState$
      .pipe(
        takeUntil(this.destroyed$),
        switchMap((loadingState) => {
          if (!loadingState.error) {
            return this.currentData$.pipe(
              map((currentData) => {
                console.log('loadingState error was false, switch to current data');
                console.log(currentData);
              })
            );
          } else {
            // loadingState.error is ture here, do something?
          }
        })
      )
      .subscribe();
  }

  ngOnDestroy() {
    this.destroyed$.next();
  }
}

Example: https://stackblitz.com/edit/angular-ivy-gjkulg?file=src%2Fapp%2Fapp.component.ts

0
Andrew Allen On

Something like the following may work in your use case. Unclear what the requirements are though.

tap can be used for side effects and this won't affect the rest of the stream - use sparingly!

const isError$ = this.loadingState.pipe(
  map(loadingState => !!loadingState.error),
  tap(isError => {
    // return to error page
  })
)

filter "stops" emissions from it's source depending on the predicate

// combine 2 observables
combineLatest([isError$, currentData]).pipe(
  // if loadingState.error is true, the operator function 2 will not carried out
  filter(([isError, data]) => !isError),

  // continue
  map(([_, data]) => {
    
  }),

)