10

For the life of me, I can not make this work. I've searched and searched, but I couldn't find any example (all examples out there are with .fromEvent(), none on a http.get).

In my template, I have this input:

<input type="text" (input)="categoriesSearch($event)">

In my component, I have the following:

categoriesSearch(event) {
    this.categoriesSubscription = this.apiService
        .getCategoriesList(this.uploadForm.get('categories').value)
        .debounceTime(3000)
        // .throttleTime(3000)
        .subscribe(
            (response) => {
                this.categories = response.data;
            }
        );
}

And this is the method in my ApiService:

getCategoriesList(keyword = null) {
    const headers = new Headers();
    headers.append('Content-Type', 'application/json');
    headers.append('Bearer', this.authService.user.token);

    const getParams: URLSearchParams = new URLSearchParams();
    getParams.set('keyword', keyword);
    return this.http.get(this.apiHost + '/categories', { headers: headers, search: getParams })
        .map(response => response.json());
}

In the categoriesSearch() method, I've tried both debounceTime() and throttleTime() (I also imported them, of course import 'rxjs/add/operator/debounceTime', import 'rxjs/add/operator/throttleTime').

But the http.get request is not debounced or throttled at all! If I type 10 characters in 3 seconds, it makes 10 http requests.

How on earth do I tell this.http.get to only make a request if at least 3 seconds have passed since the previous request or since 'no request' (meaning an initial 3 seconds delay)? Ok, maybe here I should say "since I've last typed something in my input".

I've also tried using debounceTime()/throttleTime() in the service directly, before the .map() operator - but the result is the same.

MrCroft
  • 3,049
  • 2
  • 34
  • 50

3 Answers3

16

But the http.get request is not debounced or throttled at all! If I type 10 characters in 3 seconds, it makes 10 http requests.

your implementing in a wrong way. you need capture input first, apply denounce and do HTTP request.

you can implement in several ways

1) Observable.fromEvent

 <input type="text" #input>

Component

 @ViewChild('input') text: ElementRef;

  ngAfterViewInit() {
    let text$ = Observable.fromEvent(this.text.nativeElement, 'keyup')
     .do(() => console.log("keyup"))
    .debounceTime(3000)
     .distinctUntilChanged()
    .switchMap(() => getCategoriesList())
        .subscribe(res => console.log(res));
  }

2) Using subject

<input type="text" (keyup)="search($event)">

component

  searchTerms = new Subject<string>();

search(term: string): void {
    this.searchTerms.next(term);
  }

ngOnInit(): void {

    this.searchTerms
      .debounceTime(3000)
      .distinctUntilChanged()
      .switchMap(() => getCategoriesList())
      .subscribe(term => { 
       console.log();
     });

3) Using form control

 <input type="text" [formControl]="term">

component

  term = new FormControl();

  ngOnInit() {
    this.items = this.term.valueChanges
                 .debounceTime(3000)
                 .distinctUntilChanged()
                 .switchMap(term => getCategoriesList(term))
                 .subscribe(res => console.log(res));
  }
CharanRoot
  • 6,181
  • 2
  • 27
  • 45
  • 1
    Sorry to get back so late to accept the answer. Thank you! I would have expected that the almighty observable would offer such a thing... just adding `.debounceTime()` or `.throttleTime()` before `.subscribe()` to any observable and it would automatically know what to do with it (keep track of times and moments when it's been fired etc.). Very, very disappointing :( It's a shame that I have to make yet another observable JUST for that sole purpose. Shame on RxJs!! – MrCroft May 28 '17 at 19:16
  • 1
    Actually the second way does NOT work (see how many issues on github...) – Romain Bruckert Jun 05 '17 at 13:11
  • 1
    Another working example using formControl: https://stackoverflow.com/questions/32051273/angular2-and-debounce – Romain Bruckert Jun 05 '17 at 13:22
  • 1
    @MahmoudAliKassem yes, definitely. This question is very old, from 2017, before the operators were pipe-able (I assume you meant to say RxJs 6, instead of JS6... I've just Googled and found that pipe-able operators were introduced in 5.5 actually) – MrCroft Oct 01 '21 at 10:47
  • @MrCroft thank you for the information, I will delete my answer, but it didn't work for me without using pipe, I think it was the very of RxJS as you mentioned – Mahmoud Kassem Oct 01 '21 at 14:14
0

It is important to execute the order operation, move debounceTime above getCategoriesList() and it works

Milad Jafari
  • 970
  • 10
  • 15
-5

Using form control

 <input type="text" [formControl]="term">

component

term = new FormControl();

  ngOnInit() {
    this.items = this.term.valueChanges
                 .debounceTime(3000)
                 .distinctUntilChanged()
                 .switchMap(term => getCategoriesList(term))
                 .subscribe(res => console.log(res));
  }