1

I am trying to create an observable with a state to be able to show a loading in the UI. The combineLatest works as it should, the pipe is only on the switchmap to prevent the observable (combineLatest) from closing in case of error I found an example (this or this) of how I should use a stateful observable, but I can't merge the two, any help? thx

My example

combineLatest([
    Observable_One,
    Observable_Two,
    Observable_Three,
  ]).pipe(
    switchMap(([R1, R2, R3]) =>
      this.MyServiceWithPossibleError(...)
        .pipe(catchError(() => { return EMPTY }))))

Web example

export enum ObsevableStatus {
  SUCCESS = 'Success',
  ERROR = 'Error',
  LOADING = 'Loading',
}

export interface ObservableWithStatus<T> {
  status: string;
  data?: T;
  error?: Error;
}

export function observableWithStatus<T>(x: Observable<T>): Observable<ObservableWithStatus<T>> {
  return x.pipe(
    map(x => ({ status: ObsevableStatus.SUCCESS, data: x })),
    startWith({ status: ObsevableStatus.LOADING }),
    catchError(x => {
      return of({ status: ObsevableStatus.ERROR, error: x });
    })
  );
}

UPDATE

  1. Show loading if "Observable_One", "Observable_One" or "Observable_Three" emit some value
  2. if my service (MyWebServiceWithPossibleError) throw some error, stop loading and show message, BUT not finish "combineLatest" (long-lived observable), so can continue receiving the events of the 3 observables
  3. if not error, hide loading and show data

The intention is not to literally combine the two examples, it is to build an example with both

UPDATE 2

getEntities$ = combineLatest([
    Observable_One,
    Observable_Two,
    Observable_Three,
  ]).pipe(
    switchMap(
      ([R1, R2, R3]) =>
        concat(
          of({ status: 'loading', value: '' }),
          this.MyServiceWithPossibleError$(...)
            .pipe(map(x => ({ status: 'success', value: SERVICE_RESPONSE })), catchError(() => EMPTY))
        )
    )
  )

<div *ngIf="getEntities$ | async as obs" [ngSwitch]="obs.status">
  <div *ngSwitchCase="'success'">
    Success! {{ obs.value }}
  </div>

  <div *ngSwitchCase="'error'">
    Error
  </div>

  <div *ngSwitchCase="'loading'">
    Loading!
  </div>
</div>
avechuche
  • 1,470
  • 6
  • 28
  • 45
  • From your question, it's not clear what you want. You can't merge the two: what do you mean? What two things are you trying to merge? What's the output you're expecting? What the output you're getting instead? – Mrk Sef Oct 14 '20 at 16:23
  • @MrkSef Hi!, see the update – avechuche Oct 15 '20 at 01:03
  • What do you mean "Show Loading?" - Is your observable being consumed by something that shows a loading animation? Should it be outputting a stream of boolean? Are show loading/show message/show data side effects? – Mrk Sef Oct 15 '20 at 13:07
  • @MrkSef Hi! look at my "Update 2". I almost finished what I need, but I have a problem when catching the error, I don't know how to show the corresponding div – avechuche Oct 15 '20 at 16:36

1 Answers1

2

It sounds like all you're missing is that you need to map your error into an actual response, rather than just an empty stream that completes immediately

getEntities$ = combineLatest([
  Observable_One,
  Observable_Two,
  Observable_Three
]).pipe(
  switchMap(([R1, R2, R3]) =>
    concat(
      of({ status: 'loading', value: '' }),
      this.MyServiceWithPossibleError$(...).pipe(
        map(x => ({ 
          status: 'success', 
          value: SERVICE_RESPONSE 
        })), 
        catchError(err => of({ 
          status: 'error', 
          value: 'ERROR MESSAGE: ' + err.message
        })),
      )
    )
  )
);
        
Mrk Sef
  • 7,557
  • 1
  • 9
  • 21