When I use a Observable.Interval to perform a http refresh of a UI it locks out the buttons on the UI from working if the interval is too fast. The buttons do not register the click, seems to be a timing issue. If I increase the timing and therefore miss the get call the buttons work, but the data is then delayed in updating.
Interval
this.dataSub = Observable.interval(1000).subscribe(x => {
this.getData();
})
getData
getData(): void {
this.dataService.getData()
.subscribe(
data => this.data = data,
error => console.log("Error HTTP Get Service" + this.data),
() => {});
}
Is there a best practice, or something I'm missing that I should be doing to refresh the UI but not lock out the buttons
In general, you should avoid explicitly subscribing to observables wherever possible. Instead, use all of the operators (yes, it can be very tricky to figure out the right ones) available to compose all the source/input observables into one or more observables that you use in the view with the async pipe.
Don't forget that JS is single threaded (aside from workers). Your UI and most Angular code has to share the one thread, so long running JS locks up your UI.
This has three main benefits:
ngOnDestroy() hook or when you no longer care about it, you risk creating a memory leak whenever you manually subscribe. Whereas the async pipe will unsubscribe correctly when the component/element it is used on gets destroyed - you don't need to worry about it.switchMap(), switchLatest(), etc, you can cancel and cleanup superceded HTTP calls and other expensive operations, or even stop them before they have started if they are no longer required. Don't do more than you have to. This also usually means the change detection doesn't have to run as much, which means better performance.With all that in mind, how could you apply it to your code?
One thing you may not be aware of (many people aren't) is that if your DataService.getData() method is something like:
getData(): Observable<MyData[]> {
return this.http.get('http://some.url/data').map(res => res.json());
}
then every time you subscribe to the observable created by the Http service, a new request is made. This is what you want, but what you don't want is to process the results from any previous requests as soon as a new one is made.
So you could instead just compose one observable with the latest data from the most recent request using something like this in your controller:
ngOnInit() {
// (I follow a convention where observable properties end in $)
this.data$ = Observable.interval(1000).flatMapLatest(() => {
return this.dataService.getData();
});
}
No subscriptions, just a created observable. Then in your view, just use the async pipe with the data$ property and you are golden.
For example:
<ul *ngFor="let d of (data$ | async); trackBy: d?.id">
<li>{{d.name}}</li>
</ul>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With