I have a button which makes an API call on keyboard enter. If they enter instantly multiple times, it would make 'n' no. of calls.
How to avoid this with a clean generic solution, so that it can be used everywhere?
<button click="initiateBulkPayment()(keyup.enter)="initiateBulkPayment">
initiateBulkPayment = (orderId:any, payment_methods:any) => {
let postParams = payment_methods;
console.log('this is payment_method', payment_methods);
return this.http.post(Constants.API_ENDPOINT + '/oms/api/orders/' +
orderId + '/payments/bulk_create/', postParams, this.auth.returnHeaderHandler())
.pipe(map((data: any) => {
return data;
}),
catchError((err)=>{
return throwError(err);
}));
}
There are two solutions:
Disable the button while the call is being executed:
<button [disabled]="paymentRequest.inProgress$ | async" (click)="onPayButtonClick()">
export class ProgressRequest {
private _inProgress$ = new UniqueBehaviorSubject(false);
execute<TResult>(call: () => Observable<TResult>): Observable<TResult> {
if (!this._inProgress$.value) {
this._inProgress$.next(true);
return call().pipe(
finalize(() => {
this._inProgress$.next(false);
})
);
} else {
throw new Error("the request is currently being executed");
}
}
get inProgress$(): Observable<boolean> {
return this._inProgress$;
}
}
@Component({ ... })
export class MyComponent {
readonly paymentRequest = new ProgressRequest();
onPayButtonClick() {
this.paymentRequest.execute(() => {
return this.http.post(
Constants.API_ENDPOINT + '/oms/api/orders/' + orderId + '/payments/bulk_create/',
postParams,
this.auth.returnHeaderHandler()
).pipe(map((data: any) => {
return data;
});
}).subscribe(data => {
console.log("done!", data);
});
}
}
Skip the excess calls:
You can use exhaustMap to skip requests while the previoius one is being executed. Note that switchMap and shareReplay, which was suggested in other answers won't prevent excess http calls.
<button #paymentButton>
@Component({ ... })
export class MyComponent implements OnInit {
@ViewChild('paymentButton', { static: true })
readonly paymentButton!: ElementRef<HTMLElement>;
ngOnInit() {
merge(
fromEvent(this.paymentButton.nativeElement, 'click'),
fromEvent<KeyboardEvent>(this.paymentButton.nativeElement, 'keyup').pipe(
filter(event => event.key === "Enter")
)
).pipe(
exhaustMap(() => {
return this.http.post(
Constants.API_ENDPOINT + '/oms/api/orders/' + orderId + '/payments/bulk_create/',
postParams,
this.auth.returnHeaderHandler()
).pipe(map((data: any) => {
return data;
});
})
).subscribe(result => {
console.log(result);
});
}
}
Note that click event is fired also when you press the enter key, so it isn't necessary to listen 'keyup'.
// You can replace
merge(
fromEvent(this.paymentButton.nativeElement, 'click'),
fromEvent<KeyboardEvent>(this.paymentButton.nativeElement, 'keyup').pipe(
filter(event => event.key === "Enter")
)
)
// just by
fromEvent(this.paymentButton.nativeElement, 'click')
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