Showing posts with label Typescript. Show all posts
Showing posts with label Typescript. Show all posts

How can i use async pipe to handle click or other user event ?


Almost all of the tutorials related to async pipe is related to loading data from api. But there is another thing you can do with it. Lets say you have a button which sends some data to api. The imperative code would look something like this





@Component({
selector: 'app-root',
template: `<button (click)="click()"></button>`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'test';

click() {
this.apiService.send()
.pipe(take(1)) // complete upon first emission
.subscribe(() => {})
}
}




That is, its creating a new subscription every click and completes. And if you forget to use take(1) operator you would be creating multiple subscriptions. The clean way of doing this would be to create an subject and subscribe with async pipe.





@Component({
selector: 'app-root',
template: `<ng-container *ngIf="clickStream$|async"></ng-container> <button (click)="this.clickStream.next(true)"></button>`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'test';

clickStream = new Subject();
clickStream$ = this.clickStream.asObservable()
.pipe( concatMap(() => this.apiService.send()) )

}




This snippet of code doesn't create new subscription per every click. The subscription for click event is removed when the component is destroyed.


Share:

RxJs: How to handle errors in infinitely emitting observable


Lets say i have a button which sends an api request to external server when clicked. The api requests may fail occasionally, we would want the observable to keep running even though a failure has occurred on the pipeline.





lets use this example





import {
fromEvent,
scan,
map,
of,
catchError,
EMPTY
} from 'rxjs';

let i = 0;

fromEvent(document, 'click').pipe(map(() => {
i += 1;
if (i % 2 === 0) {
throw new Error('an error occured')
}
return i;
}), catchError(() => EMPTY)).subscribe({
next: (count) => console.log('Clicked ' + count + ' times'),
error: (error) => console.log('error occured'),
complete: () => console.log('completed')
});





This will print this output upon click






Clicked 1 times





completed






As you can see if we catch an error and return EMPTY observable the subscriber completes. i want to prevent this from happening, i want it to listen to infinite click events and it should not complete.





introducing retry method ..





By definition it does the following






Returns an Observable that mirrors the source Observable with the exception of an error.


https://rxjs.dev/api/index/function/retry




so when an error occurs we just call retry() with no arguments. It is important to note that using the retry operator with no arguments can cause the observable to retry indefinitely, which can be problematic if the error is not recoverable. In such cases, it is generally better to use the catchError operator to handle the error and continue the sequence with a new observable, or to use the retry operator with a finite number of retries.





import {
fromEvent,
retry,
map,
} from 'rxjs';

let i = 0;

fromEvent(document, 'click').pipe(map(() => {
i += 1;
if (i % 2 === 0) {
console.log('error occured for click count: ' + i)
throw new Error('an error occured')
}
return i;
}), retry()).subscribe({
next: (count) => console.log('Clicked ' + count + ' times'),
error: (error) => console.log('error occured'),
complete: () => console.log('completed')
});





This subscriber will continue listening to click events even when error occurs, here is an example output from this setup





Clicked 1 times
error occured for click count: 2
Clicked 3 times
error occured for click count: 4
Clicked 5 times
error occured for click count: 6
Clicked 7 times
error occured for click count: 8
Clicked 9 times
error occured for click count: 10
Clicked 11 times
error occured for click count: 12

Share: