Reputation: 2598
I have a problem which I tought I can solve with a subscription:
refresh$: Subscription;
data$: Subscription;
ngOnInit() {
this.refresh = interval(1000).subscribe(() => {
this.getData();
}
);
}
ngOnDestroy() {
this.refresh$.unsubscribe();
this.data$.unsubscribe();
}
getData() {
this.data$ = service.getData().subscribe(response => {
// here, based on response, I update the header component value whithin an event
}, err => {
// also, if catch error, update header component
});
}
Because I have an interval at 1 seconds and the server is down (intentional), my interval will emit 5 requests in 5 seconds, but the answer foreach will came in much time as 1 second.
So, when I emit first request and wait its answer (which will throw an error), already will emit the second request, the thirs, and so on.
In this time, if I leave the page (calling ngOnDestroy
), I want to update the header from another component. But, after leaving the page, I will receive all the responses (success or failure) of the previous component. I want to cancell all these when I leave it. I thought that unsubscribing
to data$
will solve this, but the problem persist.
thanks
Upvotes: 2
Views: 1107
Reputation: 14099
You have nested subscriptions which is bad practice and makes it harder to unsubscribe from all inner subscriptions. Use an observable mapping operator like mergeMap
, switchMap
, concatMap
or exhaustMap
to map to an inner observable and use takeUntil
to unsubscribe.
private destroy$ = new Subject();
ngOnInit() {
interval(1000).pipe(
concatMap(() => this.getData()),
takeUntil(this.destroy$)
).subscribe(response => {
// update the header component value based on response
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
getData(): Observable<any> {
return service.getData().pipe(
catchError(error => {
// update header component on error
return EMPTY; // if the subscribe callback shouldn't be executed on errors
// OR return an observable with the value the subscribe callback in ngOnInit should receive
return of(/* what the header should be set to on errors */)
})
);
}
You could also use the async
pipe to subscribe and handle the subscription. Maybe use timer
instead of interval
to send the first request without an initial delay.
data$ = timer(0, 1000).pipe(
concatMap(() => this.getData()),
);
getData(): Observable<any> {
return service.getData().pipe(
catchError(error => {
return of(/* what the header should be set to on errors */)
})
);
}
<header>
<ng-container *ngIf="data$ | async as data">
{{ data }}
</ng-container>
</header>
Upvotes: 1
Reputation: 2598
I found a quickly solution, but is not the best, because this don't close the subscription.
declare a boolean pageLeaved = false
and set it true on ngOnDestroy
. then, in error case of subscription, just return if the pageLeaved
is true.
pageLeaved = false;
refresh$: Subscription;
data$: Subscription;
ngOnInit() {
this.refresh = interval(1000).subscribe(
() => {
this.getData();
}
);
}
ngOnDestroy() {
this.pageLeaved = true;
this.refresh$.unsubscribe();
this.data$.unsubscribe();
}
getData() {
this.data$ = service.getData().subscribe(response => {
// here, based on response, I update the header component value whithin an event
}, err => {
// also, if catch error, update header component
if (this.pageLeaved) {
return;
}
});
}
Please note that this is "just a momentary solution", because the error case will be called even if the observable is unsubscribed. If you have a better example,feel free to answer.
Upvotes: 0