Observable with status (for UI)

279 views Asked by At

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>
1

There are 1 answers

0
Mrk Sef On BEST ANSWER

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
        })),
      )
    )
  )
);