Tengo varios componentes en una página, todos los cuales usan observables para obtener datos de API. Paso estos observables a un servicio de carga, que quiero mostrar un cargador desde que se pasa el primer observable hasta que finaliza el último.
Servicio de carga:
private _loading = new BehaviorSubject<boolean>(false); readonly loading$ = this._loading.asObservable(); showUntilLoadingComplete<T>(observable$: Observable<T>): Observable<T> { return of(null).pipe( tap(_ => this._loading.next(true)), concatMap(_ => observable$), finalize(() => this._loading.next(false)) ); }Mis componentes luego llaman al servicio de carga así:
this.loadingService.showUntilLoadingComplete(someObservable$) .subscribe(data=> { // do stuff });Sin embargo, debido a la primera finalización observable, el sujeto de comportamiento pasa falso y esto a su vez evita que el cargador se muestre. He considerado crear otro sujeto de comportamiento para almacenar una matriz de los observables activos, y eliminarlos de aquí una vez finalizados, y luego suscribirme a eso y desactivar el cargador una vez que la matriz no tenga longitud. Pero esto no parece una gran solución, así que estoy buscando la opinión de otros.
Dado que depende de la misma loading$ Observable en un servicio único, puede agregar otra propiedad para reflejar el número active de llamadas, luego desactivar la loading solo si no hay otra llamada activa.
Prueba algo como lo siguiente:
private _active: number = 0; private _loading = new BehaviorSubject<boolean>(false); readonly loading$ = this._loading.asObservable(); showUntilLoadingComplete<T>(observable$: Observable<T>): Observable<T> { return of(null).pipe( tap(() => { this._loading.next(true); this._active++; }), concatMap((_) => observable$), finalize(() => { this._active--; if (!this._active) { this._loading.next(false); } }) ); }