Reputation: 519
I'm making HTTP calls and want to set a loading indicator before the HTTP call is made. At the moment I'm doing:
this.loadingIndicatorService.setLoadingIndicatorOn(true)
this.cop.getunMappedTechnologyDetails().pipe(
finalize(() => {
this.loadingIndicatorService.setLoadingIndicatorOn(false);
}),
).subscribe(
res => {...}
But I would like to set the loadingindicator inside the pipe if possible, so that I can be sure it runs and so that I can easily copy/paste it in front of all my subscriptions. (I've looked in to AOP, but it seems to be unpractical in this particular case)
tap doesn't work because it fetches first and then acts upon the result, but I'm showing it as visual for what I want to have:
this.cop.getunMappedTechnologyDetails().pipe(
tap(() => this.loadingIndicatorService.setLoadingIndicatorOn(true)),
finalize(() => {
this.loadingIndicatorService.setLoadingIndicatorOn(false);
}),
).subscribe(
res => {...}
I might be looking into a generic function too, but for now I need a quick solution where I could paste in these pipes in front of my subscriptions.
EDIT: I've tried Jan-Niklas Wortmann option with defer, but I get an error on .pipe:
I then tried using it inside the pipe:
this.cop.getunMappedSafeDetails().pipe(
defer(() => {
this.loadingIndicatorService.setLoadingIndicatorOn(true);
return this.cop.getunMappedSafeDetails();
}),
finalize(() => {
this.loadingIndicatorService.setLoadingIndicatorOn(false);
}),
).subscribe(...);
But I have error :
Argument of type 'Observable<String[]>' is not assignable to parameter of type 'OperatorFunction<String[], {}>'.
Type 'Observable<String[]>' provides no match for the signature '(source: Observable<String[]>): Observable<{}>'.
Upvotes: 7
Views: 5989
Reputation: 2947
Write like this:
this.cop.getunMappedTechnologyDetails().pipe(
startWithTap(() => this.loadingIndicatorService.setLoadingIndicatorOn(true)),
finalize(() => this.loadingIndicatorService.setLoadingIndicatorOn(false)),
)
Whenever you subscribe to above Observable, startWithTap
will execute whatever you pass instantly and then switch to Original Observable. Lastly, finalize will execute callback when source Observable completes.
And the custom operator for it:
import { Observable, of } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
export function startWithTap<T>(callback: () => void) {
return (source: Observable<T>) =>
of({}).pipe(tap(callback), switchMap((o) => source));
}
Upvotes: 14
Reputation: 421
you can use defer
for that purpose.
Code would look like this:
defer(() => {
this.loadingIndicatorService.setLoadingIndicatorOn(true));
return this.cop.getunMappedTechnologyDetails();
}).pipe(finalize(() => {
this.loadingIndicatorService.setLoadingIndicatorOn(false);
}),
).subscribe(
res => {...}
)
defer
is a side-effect that happens when you subscribe to this observable, therefore it's kinda the opposite of finalize
. But do notice it is every time executed when you subscribe to an observable
Upvotes: 3