Search code examples
angularrxjsbehaviorsubjectrxjs-observables

RxJS http request made multiple times


I have attached an open() method to my ng-select element in order to populate it with data from an external API.

The problem I encountered: if I open the dropdown 5 times and then I type a letter, it will make 5 http requests to the server in order to populate it with data. Same issue when I use the virtual scroll functionality.

component.html

<ng-select [items]="filterValuesBuffer"
         [typeahead]="filterValuesInput$[filter.name]"
         [virtualScroll]="true"
         [multiple]="true"
         [closeOnSelect]="false"
         [loading]="filterValuesLoading[filter.name]"
         [(ngModel)]="filter.filter_values"
         (scrollToEnd)="onScrollToEnd(filter.name)"
         (open)="onFilterOpen(filter.name)"
         typeToSearchText="No values found"
         bindLabel="name">
</ng-select>

component.ts

filterValuesInput$: Map<string, Subject<string>[]> = new Map();

onFilterOpen(filterName) {
    this.filterValuesLoading[filterName] = true;

    this.getFilterValues(filterName, '')
      .subscribe(res => {
        this.afterKey = res.after_key;
        this.filterValuesLoading[filterName] = false;
        this.filterValuesBuffer = res.filter_values;
      });
}


getFilterValues(filterName, afterKey) {
    return this.filterValuesInput$[filterName].pipe(
      tap(() => this.filterValuesLoading[filterName] = true),
      startWith(''),
      distinctUntilChanged(),
      switchMap(term  => this.search.getFilterValues(filterName, '' + term, '' + afterKey)),
    )
}

How can I prevent this behaviour?

EDIT virtual scroll:

(scrollToEnd)="fetchMore(filter.name)"

fetchMore(filterName) {
    this.filterValuesLoading[filterName] = true;

    this.getFilterValues(filterName, this.afterKey)
      .subscribe(res => {
        this.afterKey = res.after_key;
        this.filterValuesLoading[filterName] = false;
        this.filterValuesBuffer = this.filterValuesBuffer.concat(res.filter_values);
      })
  }

Solution

  • Try changing your method like below :-

    public openedFiterName = {};
    onFilterOpen(filterName) {
        this.filterValuesLoading[filterName] = true;
        if(this.openedFilterName[filterName]) {
          this.filterValuesLoading[filterName] = false;
          this.filterValuesBuffer = this.openedFilterName[filterName];
          return;
        }
        this.getFilterValues(filterName, '')
          .subscribe(res => {
            this.openedFiterName[filterName] = res.filter_values; 
            this.afterKey = res.after_key;
            this.filterValuesLoading[filterName] = false;
            this.filterValuesBuffer = res.filter_values;
          });
    }
    
    fetchMore(filterName) {
        this.filterValuesLoading[filterName] = true;
    
        this.getFilterValues(filterName, this.afterKey)
          .pipe(take(1)).subscribe(res => {
            this.afterKey = res.after_key;
            this.filterValuesLoading[filterName] = false;
            this.filterValuesBuffer = this.filterValuesBuffer.concat(res.filter_values);
          })
      }