Вот как я это сделаю:
const decorateObservable = (obs$, key) => obs$.pipe(
mapTo(false), // `false` -> no error
catchError(() => of(true)), // `true` -> error found
map(value => ({ key, value })) // Identification
)
const base$ = merge(
decorateObservable(obs1$, 'obs1'),
decorateObservable(obs2$, 'obs2'),
).pipe(
// When the source is unsubscribed(`error`/`complete`),
finalize(() => this.isLoading = false),
share(),
)
const obs1Displayed$ = base$.pipe(
filter(o => o.key === 'obs1'),
map(o => o.value),
)
const obs2Displayed$ = base$.pipe(
filter(o => o.key === 'obs2'),
map(o => o.value),
)
Оператор share()
используется здесь, поскольку нежелательна многократная подписка на источник.
В этом случае вы подписывался бы дважды (в шаблоне), потому что обе отображаемые наблюдаемые происходят из одной и той же базы. Что share
делает для многоадресной передачи производителя данных.
share
совпадает с pipe(multicast(() => new Subject()), refCount())
. refCount
указывает, что производитель будет вызван , как только появится первый подписчик.
Иными словами, потребитель данных решает, когда производитель должен начать свою логику c .
Обратите внимание, что я предположил, что obs1$
и obs2$
генерируют значения асинхронно.
По сути, это почти то же самое, что и:
const s = new Subject();
// The subscriptions happen inside the template
s.pipe(filter(o => o.key === 'obs1', ...).subscribe(observer1)
s.pipe(filter(o => o.key === 'obs2', ...).subscribe(observer2)
// And later on...
s.next({ key: 'obs1', value: false }) // `obs1$` emitted
А вот и шаблон:
<ng-container *ngIf="isLoading">
<app-loading-indicator></app-loading-indicator>
</ng-container>
<ng-container *ngIf="obs1Displayed$ | async">
<div class="error">
This Obs1 stuff could not be loaded currently
</div>
</ng-container>
<ng-container *ngIf="obs2Displayed$ | async">
<div class="error">
Technical error
</div>
More content here which is shown when at least Obs1 doesn't had an error
</div>