0

I have a toggle switch that makes an http PATCH request when it is triggered. Below you can see how it looks like:

enter image description here enter image description here

The template:

<div id="toggle-status-btn-container">
    <div class="slider" [ngClass]="{'to-right': !shopIsOpen}"></div>
    <div class="label-container">
        <label class="store-opened" (click)="updateShopSlotsStatus(true)" [ngClass]="{'toggled-opened-text': !shopIsOpen}">OPENED</label>
    </div>
    <div class="label-container">
        <label class="store-closed" (click)="updateShopSlotsStatus(false)" [ngClass]="{'toggled-closed-text': !shopIsOpen}">CLOSED</label>
    </div>
</div>

It works great, but as of right now the user can spam this toggle switch, triggering multiple http requests in a row.

What can I do to prevent this behaviour? I don't want to trigger an http request if the user is spamming this toggle switch. Maybe there's a way to only trigger the last one? Since I'm very new to RxJs I don't really know how to solve this.

Here is the method that is called by the click event, in the component .ts:

updateShopSlotsStatus(isOpen: boolean){
    this.shopIsOpen = isOpen;
    this.apiService.updateShopSlotsStatus(isOpen, this.weekDay).subscribe();
}

And the http request, in a service file:

updateShopSlotsStatus(isOpen: boolean, weekDay: string){
    const endpoint = this.url + "shipping/" + this.webzineId + "/delivery_slots" + "/";
    return this.http.patch(endpoint, {"slots": { [weekDay] : { "enabled": isOpen }}});
}

Thanks in advance.

msanford
  • 11,803
  • 11
  • 66
  • 93
Manuel Brás
  • 413
  • 7
  • 21
  • 2
    Does this answer your question? [Angular 2+ and debounce](https://stackoverflow.com/questions/32051273/angular-2-and-debounce) (The "thing" you're looking for is called _debouncing_. There are many old answers in this thread, but appropriate recent ones as well.) – msanford Jun 14 '21 at 14:23
  • Thank you for your suggestion! I'm having a hard time figuring out how I can adapt some of those answers to my question, since many of them are about form controls and inputs. – Manuel Brás Jun 14 '21 at 14:38

1 Answers1

2

You'll perhaps want to use a combination of BehaviorSubject and switchMap like in this StackBlitz.

Here, I've bound the open and close button to a function that changes a BehaviorSubjects value like so:

template:

  <button
    (click)="updateShopSlotsStatus(true)"
    [ngClass]="{'selected': shopSlotStatus$ | async}"
  >
    OPEN
  </button>
  <button
    (click)="updateShopSlotsStatus(false)"
    [ngClass]="{'selected': !(shopSlotStatus$ | async)}"
  >
    CLOSED
  </button>

ts:

  updateShopSlotsStatus(isOpen: boolean) {
    this.shopSlotStatusSubject$.next(isOpen);
  }

You could then watch for changes on the BehaviorSubject like so:

  private shopSlotStatusSubject$: BehaviorSubject<
    boolean
  > = new BehaviorSubject(false);

  // shared so only one subscription is maintained
  private readonly shopSlotStatus$ = this.shopSlotStatusSubject$
    .asObservable()
    .pipe(share());

  readonly changeSlotStatus$ = this.shopSlotStatus$.pipe(
    // you could introduce a debounceTime here if you wanted
    // debounceTime(500),
    tap(() => (this.changingStatus = true)),
    // faked API call here
    switchMap(status => this.appService.updateShopSlotsStatus(status)),
    tap(() => (this.changingStatus = false))
  );

The switchMap operator will cancel any inflight observables should the source (in this case the changeSlotStatus$ observable) emit.

I've included imports for exhaustMap and mergeMap on the Stackblitz. Change switchMap to both of those operators to see how they work differently. exhaustMap will ignore all subsequent emissions until the one in progress has completed, and mergeMap will fire off as many requests as button presses. I'll leave it up to you to decide which approach is best! You can learn more at learn rxjs. I would suggest reading up on RxJS and the different operators and how you can compose them using pipe. RxJS is good stuff.

You could then also bind to the changingStatus bool if you wanted to disable the button being pressed while a request is in flight.

deaks
  • 305
  • 2
  • 8