0

I have a form and I allow the user to click as many times as he wants on a refresh button. Of course, I use debounceTime operator but I don't know how to:

  • either cancel the previous http requests
  • or indicate to my service to return the value of the latest emission.

For example:

  • t1: click => received data in 2000ms
  • t2: click => received data in 200ms

Therefore, I will get the data from t1 moment whereas the latest one is at t2.

I've tried with pipe(last()), switchMap but I don't return data.

My component:

    this.filtersForm.valueChanges.pipe(debounceTime(500)).subscribe(
      form => {
        this.service.setFilters(form); // Set private field in service (1)
        this.onSubmit();
      }
    );

  onSubmit() {
    if (this.filtersForm.valid) {
      this.service.notifFiltersHasChanged();
    }
  }

Service:

    ctor(...) {
        this.filters$.subscribe(f => this.getData());
    }

    notifFiltersHasChanged() {
        this.filters$.next(this._filters); // (1) _filters is set by setFilters method
    }

    getData(): void {
        // ...
        this.backEndService.getAll(this._filters).subscribe(data => this._data = data);
    }

BackEndService:

    getAll(filters: any): Observable<Data> {
        return this.httpClient.get<Data>(url).pipe(last());
    }
Canopy
  • 51
  • 7
  • switchMap does exactly what you want – ukn Apr 05 '19 at 18:25
  • Look at [this](https://stackoverflow.com/questions/49152025/how-to-use-switchmap-to-cancel-pending-http-requests-and-taking-the-last-subscri) post, I think it looks a lot like what you are trying to do. – ukn Apr 05 '19 at 18:34

1 Answers1

0

The main trick is to use a single subscription (or even zero, if you'll use | async pipe in your template). So you source from an Observable and chain through your services.

Heres an updated example of yours:

Component

onDestroy$ = new Subject<void>();

constructor(){
  this.filtersForm.valueChanges.pipe(
    // accept only valid values
    filter(() => this.filtersForm.valid),
    // debounce them
    debounceTime(500),
    // when a value comes in -- we switch to service request
    // subsequent values would cancel this request
    switchMap(formValues => this.service.getData(formValues)),
    // this is needed to unsubscribe from the service
    // when component is destroyed
    takeUntil(this.onDestroy$)
  )
  .subscribe(data=>{
    // do what you need with the data
  })
}

ngOnDestroy() {
  this.onDestroy$.next(void 0);
}

Service

// service becomes stateless
// its only responsible for parsing and passing data
getData(filters): Observable<Data> {
    return this.backEndService.getAll(filters);
}

BackEndService

getAll(filters: any): Observable<Data> {
    return this.httpClient.get<Data>(url).pipe(last());
}

Another way would be to have a Subject, that you would push to. Otherwise it would be the same chaining on top of that Subject.

Hope this helps

kos
  • 5,044
  • 1
  • 17
  • 35