Home > Software design >  Debouncing In Angular Dispatching Action
Debouncing In Angular Dispatching Action

Time:07-13

How do I debounce in Angular when I try to call a network request through dispatching it? I don't want to call network request every keystroke. I applied this below but its not working.

TS

      search(searchValue: any) {
        this.loadingPlatNo = true;
        const searchTerm = searchValue.searchTerm;
    
        this.store
          .dispatch(new GetCarInformation(searchTerm || ''))
          .pipe(debounce(2000), takeUntil(this.destroy$))
          .subscribe((val) => {
            this.loadingPlatNo = false;
            this.car_informations = val.carInformation.car_informations;
          });
      }

HTML

    <ng-select
      #select
      (search)="search(select)"
      [items]="car_informations"
      bindLabel="plate_no"
      bindValue="plate_no"
      bindLabel="plate_no"
      clearAllText="Clear"
      [closeOnSelect]="true"
      [searchable]="true"
      [clearable]="true"
      [hideSelected]="false"
      [multiple]="false"
      [loading]="loadingPlateNo"
      loadingText="Loading..."
      addTagText="CREATE NEW PLATE NO: "
      [addTag]="addNewPlateNo"
      placeholder="Select plate no."
      formControlName="plate_no"
    ></ng-select>

CodePudding user response:

The operator you want is actually debounceTime(2000) as debounce(...) takes a callback function.

However, in this case they would both only debounce the response, not the request. That's because debounce delays notifications emitted by the source Observable. It does not delay subscribing to the source observable. The way you were trying to use it, debounce would actually just delay executing your callback function by two seconds after the response was received.

To debounce making an http request you need a second observable to apply the debounce before making the request. A Subject works well for this. You pipe the subject with a debounce and the api call, then call next() on every keystroke. The pipe will only make the api call if next() has not been called for the allotted debounce time.

Here's a simple example to show you the pattern:

const apiCall$ = of('pong').pipe(delay(4000)); // Simulates 4 second api call
  pingSubject$ = new Subject<void>();
  debouncedPing$ = this.pingSubject$.pipe(
    debounceTime(2000),
    switchMap(() => {
      console.log('ping');
      return apiCall$;
    })
  );

  ngOnInit() {
    this.debouncedPing$.subscribe((res) => console.log(res));
  }

  ping() {
    this.pingSubject$.next();
  }
<button (click)="ping()">PING</button>

Stackblitz: https://stackblitz.com/edit/angular-ivy-2mjjf2?file=src/app/app.component.html

This won't make the request until 2 seconds after you stop clicking the button (you will see ping in the console). Then the request will take 4 seconds to complete.

Since I used switchMap, if a new request is started before the previous has responded, the previous request will be cancelled. You can use mergeMap if you do not want to cancel ongoing requests.


Your situation is a bit more complicated since you have a parameter.

I would probably implement it like this:

  searchSubject$ = new Subject<any>();
  debouncedSearch$ = this.searchSubject$.pipe(
    debounceTime(2000),
    takeUntil(this.destroy$),
    switchMap((searchValue) => this.store.dispatch(new GetCarInformation(searchValue || '')))
  );

  ngOnInit() {
    this.debouncedSearch$.subscribe((val: any) => {
      this.loadingPlatNo = false;
      this.car_informations = val.carInformation.car_informations;
    });
  }

  search(searchValue: any) {
    this.loadingPlatNo = true;
    this.searchSubject$.next(searchValue);
  }
  • Related