Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to achieve a debounce service on input keyup event in angular2 with rxjs

So the chain is really correct but the problem is that you're creating an Observable and subscribe to it on every keyup event. That's why it prints the same value multiple times. There're simply multiple subscriptions which is not what you want to do.

There're obviously more ways to do it correctly, for example:

@Component({
  selector: 'my-app',
  template: `
    <div>
      <input type="text" (keyup)='keyUp.next($event)'>
    </div>
  `,
})
export class App implements OnDestroy {

  public keyUp = new Subject<KeyboardEvent>();

  private subscription: Subscription;

  constructor() {
    this.subscription = this.keyUp.pipe(
      map(event => event.target.value),
      debounceTime(1000),
      distinctUntilChanged(),
      mergeMap(search => of(search).pipe(
        delay(500),
      )),
    ).subscribe(console.log);
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }
}

See your updated demo: http://plnkr.co/edit/mAMlgycTcvrYf7509DOP

Jan 2019: Updated for RxJS 6


@marlin has given a great solution and it works fine in angular 2.x but with angular 6 they have started to use rxjs 6.0 version and that has some slight different syntax so here is the updated solution.

import {Component} from '@angular/core';
import {Observable, of, Subject} from 'rxjs';
import {debounceTime, delay, distinctUntilChanged, flatMap, map, tap} from 'rxjs/operators';

@Component({
    selector: 'my-app',
    template: `
        <div>
            <input type="text" (keyup)='keyUp.next($event)'>
        </div>
     `,
})
export class AppComponent {
    name: string;

    public keyUp = new Subject<string>();

    constructor() {
        const subscription = this.keyUp.pipe(
            map(event => event.target.value),
            debounceTime(1000),
            distinctUntilChanged(),
            flatMap(search => of(search).pipe(delay(500)))
        ).subscribe(console.log);
    }
}

Well, here's a basic debouncer.

ngOnInit ( ) {
        let inputBox = this.myInput.nativeElement;
        let displayDiv = this.myDisplay.nativeElement;

        let source = Rx.Observable.fromEvent ( inputBox, 'keyup' )
            .map ( ( x ) => { return x.currentTarget.value; } )
            .debounce ( ( x ) => { return Rx.Observable.timer ( 1000 ); } );

        source.subscribe (
            ( x ) => { displayDiv.innerText = x; },
            ( err ) => { console.error ( 'Error: %s', err ) },
            () => {} );
    }
}

If you set up the two indicated view children (the input and the display), you'll see the debounce work. Not sure if this doesn't do anything your does, but this basic form is (as far as I know) the straightforward way to debounce, I use this starting point quite a bit, the set of the inner text is just a sample, it could make a service call or whatever else you need it to do.


I would suggest that you check the Angular2 Tutorial that show a clean and explained example of something similar to what you're asking.

https://angular.io/docs/ts/latest/tutorial/toh-pt6.html#!#observables