Sudut dan debounce

160

Di AngularJS saya dapat melakukan debo suatu model dengan menggunakan opsi ng-model.

ng-model-options="{ debounce: 1000 }"

Bagaimana saya bisa mendebo suatu model di Angular? Saya mencoba mencari debounce di dokumen tetapi saya tidak dapat menemukan apa pun.

https://angular.io/search/#stq=debounce&stp=1

Solusinya adalah menulis fungsi debounce saya sendiri, misalnya:

import {Component, Template, bootstrap} from 'angular2/angular2';

// Annotation section
@Component({
  selector: 'my-app'
})
@Template({
  url: 'app.html'
})
// Component controller
class MyAppComponent {
  constructor() {
    this.firstName = 'Name';
  }

  changed($event, el){
    console.log("changes", this.name, el.value);
    this.name = el.value;
  }

  firstNameChanged($event, first){
    if (this.timeoutId) window.clearTimeout(this.timeoutID);
    this.timeoutID = window.setTimeout(() => {
        this.firstName = first.value;
    }, 250)
  }

}
bootstrap(MyAppComponent);

Dan html saya

<input type=text [value]="firstName" #first (keyup)="firstNameChanged($event, first)">

Tapi saya mencari fungsi build, apakah ada di Angular?

koningdavid
sumber
3
Ini mungkin relevan github.com/angular/angular/issues/1773 , belum diimplantasikan .
Eric Martinez
Periksa pos ini untuk Angular 7 dengan RxJS v6 freakyjolly.com/...
Code Spy

Jawaban:

202

Diperbarui untuk RC.5

Dengan Angular 2 kita dapat melakukan debounce menggunakan operator RxJS debounceTime()pada kontrol form yang valueChangesdapat diamati:

import {Component}   from '@angular/core';
import {FormControl} from '@angular/forms';
import {Observable}  from 'rxjs/Observable';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/throttleTime';
import 'rxjs/add/observable/fromEvent';

@Component({
  selector: 'my-app',
  template: `<input type=text [value]="firstName" [formControl]="firstNameControl">
    <br>{{firstName}}`
})
export class AppComponent {
  firstName        = 'Name';
  firstNameControl = new FormControl();
  formCtrlSub: Subscription;
  resizeSub:   Subscription;
  ngOnInit() {
    // debounce keystroke events
    this.formCtrlSub = this.firstNameControl.valueChanges
      .debounceTime(1000)
      .subscribe(newValue => this.firstName = newValue);
    // throttle resize events
    this.resizeSub = Observable.fromEvent(window, 'resize')
      .throttleTime(200)
      .subscribe(e => {
        console.log('resize event', e);
        this.firstName += '*';  // change something to show it worked
      });
  }
  ngDoCheck() { console.log('change detection'); }
  ngOnDestroy() {
    this.formCtrlSub.unsubscribe();
    this.resizeSub  .unsubscribe();
  }
} 

Plunker

Kode di atas juga termasuk contoh bagaimana cara throttle window mengubah ukuran acara, seperti yang diminta oleh @albanx dalam komentar di bawah ini.


Meskipun kode di atas mungkin merupakan cara Angular untuk melakukannya, itu tidak efisien. Setiap keystroke dan setiap peristiwa pengubahan ukuran, meskipun mereka ditolak dan dicekik, menghasilkan perubahan pendeteksian berjalan. Dengan kata lain, debouncing dan pelambatan tidak mempengaruhi seberapa sering perubahan pendeteksian berjalan . (Saya menemukan komentar GitHub oleh Tobias Bosch yang mengkonfirmasi ini.) Anda dapat melihat ini ketika Anda menjalankan plunker dan Anda melihat berapa kali ngDoCheck()dipanggil ketika Anda mengetik ke dalam kotak input atau mengubah ukuran jendela. (Gunakan tombol "x" biru untuk menjalankan plunker di jendela terpisah untuk melihat ukurannya.)

Teknik yang lebih efisien adalah membuat RxJS Dapat Diamati sendiri dari acara, di luar "zona" Angular. Dengan cara ini, deteksi perubahan tidak dipanggil setiap kali suatu peristiwa menyala. Kemudian, dalam metode panggilan balik berlangganan Anda, secara manual memicu deteksi perubahan - yaitu, Anda mengontrol kapan deteksi perubahan dipanggil:

import {Component, NgZone, ChangeDetectorRef, ApplicationRef, 
        ViewChild, ElementRef} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/throttleTime';
import 'rxjs/add/observable/fromEvent';

@Component({
  selector: 'my-app',
  template: `<input #input type=text [value]="firstName">
    <br>{{firstName}}`
})
export class AppComponent {
  firstName = 'Name';
  keyupSub:  Subscription;
  resizeSub: Subscription;
  @ViewChild('input') inputElRef: ElementRef;
  constructor(private ngzone: NgZone, private cdref: ChangeDetectorRef,
    private appref: ApplicationRef) {}
  ngAfterViewInit() {
    this.ngzone.runOutsideAngular( () => {
      this.keyupSub = Observable.fromEvent(this.inputElRef.nativeElement, 'keyup')
        .debounceTime(1000)
        .subscribe(keyboardEvent => {
          this.firstName = keyboardEvent.target.value;
          this.cdref.detectChanges();
        });
      this.resizeSub = Observable.fromEvent(window, 'resize')
        .throttleTime(200)
        .subscribe(e => {
          console.log('resize event', e);
          this.firstName += '*';  // change something to show it worked
          this.cdref.detectChanges();
        });
    });
  }
  ngDoCheck() { console.log('cd'); }
  ngOnDestroy() {
    this.keyupSub .unsubscribe();
    this.resizeSub.unsubscribe();
  }
} 

Plunker

Saya menggunakan ngAfterViewInit()bukannya ngOnInit()untuk memastikan bahwa inputElRefitu didefinisikan.

detectChanges()akan menjalankan deteksi perubahan pada komponen ini dan anak-anaknya. Jika Anda lebih suka menjalankan deteksi perubahan dari komponen root (yaitu, jalankan pemeriksaan deteksi perubahan penuh) kemudian gunakan ApplicationRef.tick(). (Saya melakukan panggilan ke ApplicationRef.tick()dalam komentar di plunker.) Perhatikan bahwa panggilan tick()akan menyebabkan ngDoCheck()dipanggil.

Mark Rajcok
sumber
2
@ Mark Rajcok Saya pikir alih-alih [nilai], Anda harus menggunakan [ngModel], karena [nilai] tidak memperbarui nilai input.
Milad
1
apakah ada metode debounce umum (misalnya untuk diterapkan pada peristiwa ukuran jendela)?
albanx
1
@MarkRajcok Saya percaya masalah CD yang Anda jelaskan dalam jawaban Anda diselesaikan oleh github.com/angular/zone.js/pull/843
Jefftopia
2
Kapan kita perlu berhenti berlangganan untuk mencegah kebocoran memori?
fitnah
1
@slanden Ya, sesuai dengan netbasal.com/ ketika- ke- unsubscribe -in- angular - d61c6b21bad3 , kita harus berhenti berlangganan dari .fromEvent()langganan
Jon Onstott
153

Jika Anda tidak ingin berurusan @angular/forms, Anda bisa menggunakan RxJS Subjectdengan bindings perubahan.

view.component.html

<input [ngModel]='model' (ngModelChange)='changed($event)' />

view.component.ts

import { Subject } from 'rxjs/Subject';
import { Component }   from '@angular/core';
import 'rxjs/add/operator/debounceTime';

export class ViewComponent {
    model: string;
    modelChanged: Subject<string> = new Subject<string>();

    constructor() {
        this.modelChanged
            .debounceTime(300) // wait 300ms after the last event before emitting last event
            .distinctUntilChanged() // only emit if value is different from previous value
            .subscribe(model => this.model = model);
    }

    changed(text: string) {
        this.modelChanged.next(text);
    }
}

Ini memicu deteksi perubahan. Untuk cara yang tidak memicu deteksi perubahan, periksa jawaban Mark.


Memperbarui

.pipe(debounceTime(300), distinctUntilChanged()) diperlukan untuk rxjs 6.

Contoh:

   constructor() {
        this.modelChanged.pipe(
            debounceTime(300), 
            distinctUntilChanged())
            .subscribe(model => this.model = model);
    }
0xcaff
sumber
5
Saya lebih suka solusi ini! Bekerja dengan sudut 2.0.0, rxjs 5.0.0-beta 12
alsco77
2
Bekerja dengan sempurna, sederhana dan jelas, tidak ada bentuk yang terlibat. Saya di Angular 4.1.3, rxjs 5.1.1
kelima
Saya pikir ini adalah solusi yang unggul karena memiliki opsi untuk bekerja dengan formulir jika diperlukan, tetapi menghilangkan ketergantungan yang membuat implementasi yang jauh lebih sederhana. Terima kasih.
Maks
2
.pipe(debounceTime(300), distinctUntilChanged())diperlukan untuk rxjs 6
Icycool
Solusinya menyelamatkan saya. Saya menggunakan keyUpacara di input.nativeElementdalam mat-table, yang berhenti berfungsi ketika jumlah kolom diubah
igorepst
35

Itu bisa diimplementasikan sebagai Petunjuk

import { Directive, Input, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core';
import { NgControl } from '@angular/forms';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import { Subscription } from 'rxjs';

@Directive({
  selector: '[ngModel][onDebounce]',
})
export class DebounceDirective implements OnInit, OnDestroy {
  @Output()
  public onDebounce = new EventEmitter<any>();

  @Input('debounce')
  public debounceTime: number = 300;

  private isFirstChange: boolean = true;
  private subscription: Subscription;

  constructor(public model: NgControl) {
  }

  ngOnInit() {
    this.subscription =
      this.model.valueChanges
        .debounceTime(this.debounceTime)
        .distinctUntilChanged()
        .subscribe(modelValue => {
          if (this.isFirstChange) {
            this.isFirstChange = false;
          } else {
            this.onDebounce.emit(modelValue);
          }
        });
  }

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

}

gunakan seperti

<input [(ngModel)]="value" (onDebounce)="doSomethingWhenModelIsChanged($event)">

sampel komponen

import { Component } from "@angular/core";

@Component({
  selector: 'app-sample',
  template: `
<input[(ngModel)]="value" (onDebounce)="doSomethingWhenModelIsChanged($event)">
<input[(ngModel)]="value" (onDebounce)="asyncDoSomethingWhenModelIsChanged($event)">
`
})
export class SampleComponent {
  value: string;

  doSomethingWhenModelIsChanged(value: string): void {
    console.log({ value });
  }

  async asyncDoSomethingWhenModelIsChanged(value: string): Promise<void> {
    return new Promise<void>(resolve => {
      setTimeout(() => {
        console.log('async', { value });
        resolve();
      }, 1000);
    });
  }
} 
Oleg Polezky
sumber
1
dengan lebih banyak impor, yang berfungsi untuk saya: impor "rxjs / add / operator / debounceTime"; impor "rxjs / add / operator / differUntilChanged";
Sbl
2
Sejauh ini membuatnya menjadi paling sederhana untuk mengimplementasikan aplikasi wide
joshcomley
1
isFirstChange digunakan untuk tidak memancarkan inisialisasi
Oleg Polezky
2
Bekerja di Angular 8 dan rxjs 6.5.2 dengan perubahan berikut. Jika Anda ingin menggunakan sintaksis pipa, ubah yang berikut: import 'rxjs/add/operator/debounceTime'; import 'rxjs/add/operator/distinctUntilChanged';ke import { debounceTime, distinctUntilChanged } from 'rxjs/operators';dan this.model.valueChanges .debounceTime(this.debounceTime) .distinctUntilChanged()kethis.model.valueChanges .pipe( debounceTime(this.debounceTime), distinctUntilChanged() )
kumaheiyama
1
Berfungsi di Angular 9 dan rxjs 6.5.4 dengan perubahan @kumaheiyama dinyatakan dalam komentarnya. Hanya saja, jangan lupa untuk mengekspor arahan dalam modul tempat Anda membuatnya. Dan jangan lupa untuk memasukkan modul tempat Anda membuat direktif ini, ke dalam modul tempat Anda menggunakannya.
Filip Savic
29

Karena topik sudah tua, sebagian besar jawaban tidak berfungsi pada Angular 6/7/8/9 dan / atau menggunakan lib lainnya.
Jadi di sini adalah solusi singkat dan sederhana untuk Angular 6+ dengan RxJS.

Impor barang-barang yang diperlukan terlebih dahulu:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';

Inisialisasi pada ngOnInit:

export class MyComponent implements OnInit, OnDestroy {
  public notesText: string;
  private notesModelChanged: Subject<string> = new Subject<string>();
  private notesModelChangeSubscription: Subscription

  constructor() { }

  ngOnInit() {
    this.notesModelChangeSubscription = this.notesModelChanged
      .pipe(
        debounceTime(2000),
        distinctUntilChanged()
      )
      .subscribe(newText => {
        this.notesText = newText;
        console.log(newText);
      });
  }

  ngOnDestroy() {
    this.notesModelChangeSubscription.unsubscribe();
  }
}

Gunakan cara ini:

<input [ngModel]='notesText' (ngModelChange)='notesModelChanged.next($event)' />

PS: Untuk solusi yang lebih kompleks dan efisien Anda mungkin masih ingin memeriksa jawaban lain.

Hanya Bayangan
sumber
1
Mengapa Anda tidak berhenti berlangganan ini saat menghancurkan?
Virendra Singh Rathore
Diperbarui. Terima kasih telah memperhatikan!
Just Shadow
1
@JustShadow Terima kasih! Itu sangat membantu.
Niral Munjariya
Ini berfungsi sempurna pada percobaan pertama. Tetapi ketika saya menghapus teks yang dicari entah bagaimana permintaan berikutnya terlalu lama untuk merespon.
Sadiksha Gautam
Itu aneh. Itu masih berfungsi dengan baik di sisi saya. Bisakah Anda berbagi informasi lebih lanjut atau mungkin membuka pertanyaan baru untuk itu?
Just Shadow
28

Tidak dapat diakses langsung seperti di angular1 tetapi Anda dapat dengan mudah bermain dengan NgFormControl dan RxJS yang bisa diamati:

<input type="text" [ngFormControl]="term"/>

this.items = this.term.valueChanges
  .debounceTime(400)
  .distinctUntilChanged()
  .switchMap(term => this.wikipediaService.search(term));

Posting blog ini menjelaskannya dengan jelas: http://blog.thoughtram.io/angular/2016/01/06/mengambil manfaat dariof-observables-in-angular2.html

Ini untuk autocomplete tetapi berfungsi semua skenario.

bertrandg
sumber
tetapi ada kesalahan dari layanan, ini tidak berjalan lagi
Arun Tyagi
Saya tidak mengerti contohnya. [...] adalah target mengikat satu arah. Mengapa wadah dapat diberitahukan valueChanges? seharusnya tidak harus menjadi sth. suka (ngFormControl)="..."?
phil294
20

Anda dapat membuat RxJS (v.6) Dapat Diamati yang melakukan apa pun yang Anda suka.

view.component.html

<input type="text" (input)="onSearchChange($event.target.value)" />

view.component.ts

import { Observable } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';

export class ViewComponent {
    searchChangeObserver;

  onSearchChange(searchValue: string) {

    if (!this.searchChangeObserver) {
      Observable.create(observer => {
        this.searchChangeObserver = observer;
      }).pipe(debounceTime(300)) // wait 300ms after the last event before emitting last event
        .pipe(distinctUntilChanged()) // only emit if value is different from previous value
        .subscribe(console.log);
    }

    this.searchChangeObserver.next(searchValue);
  }  


}
Matthias
sumber
Terima kasih yang telah membantu, namun saya pikir impor harus dari rsjs/Rx, saya memiliki kesalahan ketika menggunakan impor seperti yang Anda tulis ... jadi dalam kasus saya sekarang:import { Observable } from 'rxjs/Rx';
ghiscoding
2
@ghiscoding Tergantung pada versi rxjs. Dalam versi 6 itu adalah: import { Observable } from 'rxjs';.
Matthias
Terima kasih! Sebagai tambahan, Anda hanya dapat menggunakan satu pipepanggilanpipe(debounceTime(300), distinctUntilChanged())
al.
1
searchChangeObserver adalah Subscriber, jadi searchChangeSubscriber akan menjadi nama yang lebih baik.
Khonsort
12

Bagi siapa pun yang menggunakan lodash, sangat mudah untuk mendebo fungsi apa pun:

changed = _.debounce(function() {
    console.log("name changed!");
}, 400);

lalu lemparkan sesuatu seperti ini ke template Anda:

<(input)="changed($event.target.value)" />
Brad C
sumber
3
atau hanya (input) = "diubah ($ event.target.value)"
Jamie Kudla
1
Terima kasih telah menjawab dengan lodash :)
Vamsi
Saya percaya ini masih akan memicu deteksi perubahan sudut pada setiap perubahan, terlepas dari debouncing.
AsGoodAsItGets
5

Solusi dengan pelanggan inisialisasi langsung dalam fungsi acara:

import {Subject} from 'rxjs';
import {debounceTime, distinctUntilChanged} from 'rxjs/operators';

class MyAppComponent {
    searchTermChanged: Subject<string> = new Subject<string>();

    constructor() {
    }

    onFind(event: any) {
        if (this.searchTermChanged.observers.length === 0) {
            this.searchTermChanged.pipe(debounceTime(1000), distinctUntilChanged())
                .subscribe(term => {
                    // your code here
                    console.log(term);
                });
        }
        this.searchTermChanged.next(event);
    }
}

Dan html:

<input type="text" (input)="onFind($event.target.value)">
Serhii Vasko
sumber
Bekerja sangat baik untuk kotak teks autocomplete sudut 8 utama. Terima kasih banyak.
Jasmin Akther Suma
4

Saya memecahkan ini dengan menulis dekorator debounce. Masalah yang dijelaskan dapat diselesaikan dengan menerapkan @debounceAccessor ke set accessor properti.

Saya juga telah menyediakan dekorator debounce tambahan untuk metode, yang dapat berguna untuk acara lain.

Ini membuatnya sangat mudah untuk melakukan debug properti atau metode. Parameternya adalah jumlah milidetik yang harus didebet ulang, 100 ms dalam contoh di bawah ini.

@debounceAccessor(100)
set myProperty(value) {
  this._myProperty = value;
}


@debounceMethod(100)
myMethod (a, b, c) {
  let d = a + b + c;
  return d;
}

Dan ini kode untuk dekorator:

function debounceMethod(ms: number, applyAfterDebounceDelay = false) {

  let timeoutId;

  return function (target: Object, propName: string, descriptor: TypedPropertyDescriptor<any>) {
    let originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
      if (timeoutId) return;
      timeoutId = window.setTimeout(() => {
        if (applyAfterDebounceDelay) {
          originalMethod.apply(this, args);
        }
        timeoutId = null;
      }, ms);

      if (!applyAfterDebounceDelay) {
        return originalMethod.apply(this, args);
      }
    }
  }
}

function debounceAccessor (ms: number) {

  let timeoutId;

  return function (target: Object, propName: string, descriptor: TypedPropertyDescriptor<any>) {
    let originalSetter = descriptor.set;
    descriptor.set = function (...args: any[]) {
      if (timeoutId) return;
      timeoutId = window.setTimeout(() => {
        timeoutId = null;
      }, ms);
      return originalSetter.apply(this, args);
    }
  }
}

Saya menambahkan parameter tambahan untuk dekorator metode yang memungkinkan Anda memicu metode SETELAH penundaan debounce. Saya melakukan itu sehingga saya bisa menggunakannya misalnya ketika digabungkan dengan mouseover atau mengubah ukuran acara, di mana saya ingin menangkap terjadi pada akhir aliran acara. Namun dalam kasus ini, metode ini tidak akan mengembalikan nilai.

Fredrik_Macrobond
sumber
3

Kita dapat membuat arahan [debounce] yang menimpa fungsi viewToModelUpdate dari ngModel dengan yang kosong.

Kode Arahan

@Directive({ selector: '[debounce]' })
export class MyDebounce implements OnInit {
    @Input() delay: number = 300;

    constructor(private elementRef: ElementRef, private model: NgModel) {
    }

    ngOnInit(): void {
        const eventStream = Observable.fromEvent(this.elementRef.nativeElement, 'keyup')
            .map(() => {
                return this.model.value;
            })
            .debounceTime(this.delay);

        this.model.viewToModelUpdate = () => {};

        eventStream.subscribe(input => {
            this.model.viewModel = input;
            this.model.update.emit(input);
        });
    }
}

Bagaimana cara menggunakannya

<div class="ui input">
  <input debounce [delay]=500 [(ngModel)]="myData" type="text">
</div>
BebbaPig
sumber
2

File HTML:

<input [ngModel]="filterValue"
       (ngModelChange)="filterValue = $event ; search($event)"
        placeholder="Search..."/>

File TS:

timer = null;
time = 250;
  search(searchStr : string) : void {
    clearTimeout(this.timer);
    this.timer = setTimeout(()=>{
      console.log(searchStr);
    }, time)
  }
Yoav Schniederman
sumber
2

Solusi sederhana adalah dengan membuat arahan yang dapat Anda terapkan pada kontrol apa pun.

import { Directive, ElementRef, Input, Renderer, HostListener, Output, EventEmitter } from '@angular/core';
import { NgControl } from '@angular/forms';

@Directive({
    selector: '[ngModel][debounce]',
})
export class Debounce 
{
    @Output() public onDebounce = new EventEmitter<any>();

    @Input('debounce') public debounceTime: number = 500;

    private modelValue = null;

    constructor(public model: NgControl, el: ElementRef, renderer: Renderer){
    }

    ngOnInit(){
        this.modelValue = this.model.value;

        if (!this.modelValue){
            var firstChangeSubs = this.model.valueChanges.subscribe(v =>{
                this.modelValue = v;
                firstChangeSubs.unsubscribe()
            });
        }

        this.model.valueChanges
            .debounceTime(this.debounceTime)
            .distinctUntilChanged()
            .subscribe(mv => {
                if (this.modelValue != mv){
                    this.modelValue = mv;
                    this.onDebounce.emit(mv);
                }
            });
    }
}

penggunaan akan

<textarea [ngModel]="somevalue"   
          [debounce]="2000"
          (onDebounce)="somevalue = $event"                               
          rows="3">
</textarea>
Ashg
sumber
Kelas ini jauh dari kompilasi Angular 7.
Stephane
1

Menghabiskan berjam-jam untuk hal ini, semoga saya dapat menghemat waktu bagi orang lain. Bagi saya pendekatan berikut untuk menggunakan debouncekontrol lebih intuitif dan lebih mudah dipahami bagi saya. Itu dibangun di atas solusi angular.io docs untuk pelengkapan otomatis tetapi dengan kemampuan bagi saya untuk mencegat panggilan tanpa harus bergantung pada pengikatan data ke DOM.

Plunker

Skenario penggunaan kasus untuk ini mungkin memeriksa nama pengguna setelah itu diketik untuk melihat apakah seseorang telah mengambilnya, lalu memperingatkan pengguna.

Catatan: jangan lupa, (blur)="function(something.value)mungkin lebih masuk akal untuk Anda tergantung pada kebutuhan Anda.

Helzgate
sumber
1

DebounceTime di Angular 7 dengan RxJS v6

Tautan Sumber

Demo Link

masukkan deskripsi gambar di sini

Di Template HTML

<input type="text" #movieSearchInput class="form-control"
            placeholder="Type any movie name" [(ngModel)]="searchTermModel" />

Dalam komponen

    ....
    ....
    export class AppComponent implements OnInit {

    @ViewChild('movieSearchInput') movieSearchInput: ElementRef;
    apiResponse:any;
    isSearching:boolean;

        constructor(
        private httpClient: HttpClient
        ) {
        this.isSearching = false;
        this.apiResponse = [];
        }

    ngOnInit() {
        fromEvent(this.movieSearchInput.nativeElement, 'keyup').pipe(
        // get value
        map((event: any) => {
            return event.target.value;
        })
        // if character length greater then 2
        ,filter(res => res.length > 2)
        // Time in milliseconds between key events
        ,debounceTime(1000)        
        // If previous query is diffent from current   
        ,distinctUntilChanged()
        // subscription for response
        ).subscribe((text: string) => {
            this.isSearching = true;
            this.searchGetCall(text).subscribe((res)=>{
            console.log('res',res);
            this.isSearching = false;
            this.apiResponse = res;
            },(err)=>{
            this.isSearching = false;
            console.log('error',err);
            });
        });
    }

    searchGetCall(term: string) {
        if (term === '') {
        return of([]);
        }
        return this.httpClient.get('http://www.omdbapi.com/?s=' + term + '&apikey=' + APIKEY,{params: PARAMS.set('search', term)});
    }

    }
Kode Mata-mata
sumber
1

Anda juga bisa menyelesaikan ini dengan menggunakan dekorator, Misalnya dengan menggunakan dekorator debounce dari utils-decorator lib ( npm install utils-decorators):

import {debounce} from 'utils-decorators';

class MyAppComponent {

  @debounce(500)
  firstNameChanged($event, first) {
   ...
  }
}
vlio20
sumber
0

Ini adalah solusi terbaik yang saya temukan sampai sekarang. Update ngModelpada blurdandebounce

import { Directive, Input, Output, EventEmitter,ElementRef } from '@angular/core';
import { NgControl, NgModel } from '@angular/forms';
import 'rxjs/add/operator/debounceTime'; 
import 'rxjs/add/operator/distinctUntilChanged';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/map';

@Directive({
    selector: '[ngModel][debounce]',
})
export class DebounceDirective {
    @Output()
    public onDebounce = new EventEmitter<any>();

    @Input('debounce')
    public debounceTime: number = 500;

    private isFirstChange: boolean = true;

    constructor(private elementRef: ElementRef, private model: NgModel) {
    }

    ngOnInit() {
        const eventStream = Observable.fromEvent(this.elementRef.nativeElement, 'keyup')
            .map(() => {
                return this.model.value;
            })
            .debounceTime(this.debounceTime);

        this.model.viewToModelUpdate = () => {};

        eventStream.subscribe(input => {
            this.model.viewModel = input;
            this.model.update.emit(input);
        });
    }
}

seperti yang dipinjam dari https://stackoverflow.com/a/47823960/3955513

Kemudian dalam HTML:

<input [(ngModel)]="hero.name" 
        [debounce]="3000" 
        (blur)="hero.name = $event.target.value"
        (ngModelChange)="onChange()"
        placeholder="name">

Di blur model secara eksplisit diperbarui menggunakan javascript biasa.

Contoh di sini: https://stackblitz.com/edit/ng2-debounce-working

Shyamal Parikh
sumber