I have two Observables. The rendering of the template should only start when BOTH Observables are completed or failed:
(Ignore the <any>
type, it's only for simplification here)
Component:
@Component({
selector: 'app-page',
templateUrl: './page.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PageComponent implements OnInit {
obs1$ = new Subject<any>();
obs2$ = new Subject<any>();
isLoading = true;
isObs1Error: boolean;
isObs2Error: boolean;
ngOnInit() {
this.initializeDataRetrieval();
}
initializeDataRetrieval() {
this.obs1$ = this.obs1Method();
this.obs1$.subscribe((response: any) => {
this.isObs1Error = false;
this.obs1 = response;
this.obs2$ = this.obs2Method();
this.obs2$.subscribe((response: any) => {
this.isObs2Error = false;
this.isLoading = false;
this.obs2 = response;
this.cdr.detectChanges();
});
});
}
private obs1Method(): any {
return this.obs1Service
.getStuff()
.pipe(
catchError(() => {
this.isError = true;
this.isLoading = false;
this.cdr.detectChanges();
return EMPTY;
})
);
}
private obs2Method(): any {
return this.obs2Service
.getStuff()
.pipe(
catchError(() => {
this.isObs2Error = true;
this.isLoading = false;
this.cdr.detectChanges();
return EMPTY;
})
);
}
canDisplayContent(): boolean {
return !this.isLoading && !this.isObs1Error;
}
Template:
<ng-container *ngIf="isLoading">
<app-loading-indicator></app-loading-indicator>
</ng-container>
<ng-container *ngIf="isObs1Error">
<div class="error">
This Obs1 stuff could not be loaded currently
</div>
</ng-container>
<ng-container *ngIf="canDisplayContent()">
<div class="error" *ngIf="isObs2Error">
Technical error
</div>
More content here which is shown when at least Obs1 doesn't had an error
</div>
So basically:
ng-container
with the Obs2 error messageI'm sure the TS code can be simplified by the usage of ... which RxJS operator? Although reading through RxJS Operators for Dummies: forkJoin, zip, combineLatest, withLatestFrom I'm not sure if any of these fits. As far as I understood e.g. combineLatest
only succeeds when both streams complete successfully ...
Any hint is welcome, thanks.
I would consider to use forkJoin
for this case.
The code would look like this
forkJoin(this.obs1Method(), this.obs2Method()).subscribe(
({resp1, resp2}) => {
this.isLoading = false;
this.obs2 = resp1;
this.obs2 = resp2;
this.cdr.detectChanges()
}
)
You would have probably also to change slightly the obsxMethod
s adding a tap
to set the error properties to false in case of successful retrieval of data and remove the settings which are performed within the subscribe
, like this
private obs1Method(): any {
return this.obs1Service
.getStuff()
.pipe(
tap(() => this.isError = false),
catchError(() => {
this.isError = true;
return EMPTY;
})
);
}
private obs2Method(): any {
return this.obs2Service
.getStuff()
.pipe(
tap(() => this.isObs2Error = false),
catchError(() => {
this.isObs2Error = true;
return EMPTY;
})
);
}