-1

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

user1938143
  • 1,022
  • 2
  • 22
  • 46

2 Answers2

0

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

Joosep Parts
  • 5,372
  • 2
  • 8
  • 33
  • that seems like gut, but I have a problem to use it, becase I use this code in a canActivate with return value UrlTree or Boolean. the loadingState return a observable LoadingState – user1938143 Mar 16 '23 at 08:51
  • Please provide a full explanation with more relevant code. Your question did not cover that you wanted to use an Observable for an authGuard `canActivate` – Joosep Parts Mar 16 '23 at 09:33
0

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]) => {
    
  }),

)
Andrew Allen
  • 6,512
  • 5
  • 30
  • 73