StackBlitz: https://stackblitz.com/edit/angular-ivy-s8mzka
I've developed an Angular template that utilizes the `async` pipe to subscribe to an Observable. This Observable is generated by:
- Subscription to an NgRx Store Selector (which provides selected user filters)
- Switch-mapping to an API request using the selected user filters
- Executing various operators such as `map`, `filter`, etc. after the `switchMap` operation
- The steps 2 and 3 may repeat multiple times
The API request within `switchMap` might fail due to numerous reasons, resulting in an error notification.
We aim to address this error by displaying an error alert. This error alert should also be displayed by subscribing to another Observable with the `async` pipe. The error-path Observable should directly stem from the success-path Observable without involving intermediate Subjects or any side effects.
Challenges encountered with proposed solutions:
- `catchError` or `materialize` within `switchMap`: Wrapping the error in another object alters the data structure of the notification, making it difficult to use subsequent filters like `map`, `filter`, etc. Operators such as `debounceTime` or `delay` can't directly handle the error since it's treated as a `next` notification.
- `retry`: Since the source Observable is a replaying Subject, this will trigger a new API call upon each resubscription, potentially leading to an infinite loop if the server consistently responds with errors. Moreover, forwarding the error notification using `retry` becomes challenging.
- Dispatching actions, emitting a Subject, or setting `this.error` within a `tap`: Incorporating side effects for a simple scenario like this seems unnecessary and contradictory to functional design principles.
product.service.ts
getProducts$() {
return this.store.select(selectProductFilters).pipe(
switchMap(filters => this.http.get("/api/products?" + encodeFilters(filters))
// ... map, filter, delay
);
}
products.component.ts
products$: Observable<Product[]>
error$: Observable<string>
ngOnInit() {
this.products$ = this.productService.getProducts$();
this.error$ = this.products$.pipe(
// what can we do here to receive errors as notifications
);
}
products.component.html
<div *ngIf="(products$ | async) as products">{{products | json}}</div>
<div class="error" *ngIf="(error$ | async) as error">{{error}}</div>
Therefore, the question arises:
How can we establish two separate Observables for our template - one emitting `next` notifications and the other emitting `error` notifications while keeping the source Observable operational even after encountering errors?
EDIT
We seek a comprehensive solution to this issue - while the example above involves just one `switchMap`, the resolution should be applicable to any Observable pipeline scenario. For instance, envision the Service structured as follows:
product.service.ts
getProducts$() {
return this.store.select(selectProductFilters).pipe(
switchMap(filters => this.http.get("/api/products?" + encodeFilters(filters))
// ... map, filter, delay
switchMap(...)
// ... map, filter, delay
switchMap(...)
);
}
The solution must effectively manage errors occurring in any of these `switchMap` statements and relay them to the template.