0

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);
      })
  }
shAkur
  • 944
  • 2
  • 21
  • 45

2 Answers2

1

That's because you never unsubscribe from getFilterValues(). You should bind to (close) probably and unsubscribe there:

onFilterOpen(filterName) {
  this.subscription = this.getFilterValues(filterName, '')
    .subscribe(res => ...);
}

onFilterClose() {
  this.subscription.unsubscribe();
}

You could eventually use takeWhile() operator. See Angular/RxJs When should I unsubscribe from `Subscription`

martin
  • 93,354
  • 25
  • 191
  • 226
  • Thank your for the comment. Do you know how can I unsubscribe from the the virtual scroll as well? Please check my updated question – shAkur May 25 '20 at 07:15
  • Exactly the same way as you'd do with `(close)`. Probably also on `(close)` event as well. – martin May 25 '20 at 07:25
  • Unfortunately the issue is reproducing when I scroll multiple times then I type a letter. – shAkur May 25 '20 at 07:32
1

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);
      })
  }
Aakash Garg
  • 10,649
  • 2
  • 7
  • 25
  • Tried before but I want the dropdowns populated with data even if the user doesn't searched for anything. – shAkur May 25 '20 at 07:25
  • Thanks for your answer. Unfortunately I'm using multiple dropdowns (`[filterName]`) and with this approach it only loads data (make http request) for the first dropdown opened only. – shAkur May 25 '20 at 07:38
  • Updated above, now check? – Aakash Garg May 25 '20 at 07:47
  • Yeah it works fine! Do you know if this approach could be used to prevent multiple calls when using virtual scroll? I couldn't make it to work there unfortunately – shAkur May 25 '20 at 07:57
  • on virtual scroll you will be needing calls to load more data right? why want to prevent it? – Aakash Garg May 25 '20 at 07:58
  • Let's say I scroll to the end of dropdown for 3 times. It will make 3 http requests for loading new data. Nothings wrong here but then If I type letter 'c' it will make another 3 requests for loading data using this letter. How can I prevent that from happening? – shAkur May 25 '20 at 08:01
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/214556/discussion-between-shakur-and-aakash-garg). – shAkur May 25 '20 at 08:05