Kątowy i odbicia

160

W AngularJS mogę odrzucić model używając opcji ng-model.

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

Jak mogę usunąć model w Angular? Próbowałem poszukać odbicia w dokumentach, ale nie mogłem nic znaleźć.

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

Rozwiązaniem byłoby napisanie własnej funkcji debounce, na przykład:

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);

A mój html

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

Ale ja szukam wbudowanej funkcji, czy jest taka w Angular?

koningdavid
źródło
3
Może to być istotne github.com/angular/angular/issues/1773 , jeszcze najwyraźniej nie zaimplementowane.
Eric Martinez
Sprawdzić ten post kątowego 7 z RxJS v6 freakyjolly.com/...
Code Spy

Odpowiedzi:

202

Zaktualizowano dla RC.5

W Angular 2 możemy odrzucić za pomocą operatora RxJS debounceTime()na valueChangesobserwowalnej kontrolce formularza :

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

Powyższy kod zawiera również przykład ograniczania zdarzeń związanych ze zmianą rozmiaru okna, zgodnie z pytaniem @albanx w komentarzu poniżej.


Chociaż powyższy kod jest prawdopodobnie sposobem Angulara, nie jest efektywny. Każde naciśnięcie klawisza i każde zdarzenie zmiany rozmiaru, nawet jeśli są usuwane i ograniczane, powoduje uruchomienie wykrywania zmian. Innymi słowy, debouncing i throttling nie wpływają na częstotliwość wykrywania zmian . (Znalazłem komentarz na GitHub autorstwa Tobiasa Boscha, który to potwierdza.) Możesz to zobaczyć po uruchomieniu plunkera i zobaczyć, ile razy ngDoCheck()jest wywoływana, gdy wpiszesz w pole wprowadzania lub zmienisz rozmiar okna. (Użyj niebieskiego przycisku „x”, aby uruchomić plunker w osobnym oknie, aby zobaczyć zdarzenia zmiany rozmiaru).

Bardziej wydajną techniką jest samodzielne tworzenie RxJS Observables na podstawie zdarzeń, poza „strefą” Angulara. Dzięki temu wykrywanie zmian nie jest wywoływane za każdym razem, gdy wystąpi zdarzenie. Następnie w metodach wywołania zwrotnego subskrypcji ręcznie wyzwalaj wykrywanie zmian - tj. Kontrolujesz, kiedy wywoływane jest wykrywanie zmian:

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

Używam ngAfterViewInit()zamiast, ngOnInit()aby upewnić się, że inputElRefjest zdefiniowane.

detectChanges()uruchomi wykrywanie zmian dla tego składnika i jego elementów podrzędnych. Jeśli wolisz uruchomić wykrywanie zmian ze składnika głównego (tj. Uruchomić pełne sprawdzenie wykrywania zmian), użyj ApplicationRef.tick()zamiast tego. ( ApplicationRef.tick()Wstawiłem call to w komentarzach w plunkerze.) Zauważ, że wywołanie tick()spowoduje, że ngDoCheck()zostaniesz wezwany.

Mark Rajcok
źródło
2
@Mark Rajcok Myślę, że zamiast [wartość], powinieneś użyć [ngModel], ponieważ [wartość] nie aktualizuje wartości wejściowej.
Milad
1
czy jest jakaś ogólna metoda odbicia (na przykład do zastosowania w przypadku zmiany rozmiaru okna)?
albanx
1
@MarkRajcok Uważam, że problem z płytą CD, który opisałeś w swojej odpowiedzi, został rozwiązany przez github.com/angular/zone.js/pull/843
Jefftopia,
2
Kiedy musielibyśmy zrezygnować z subskrypcji, aby zapobiec wyciekom pamięci?
slanden
1
@slanden Tak, zgodnie z netbasal.com/when-to-unsubscribe-in-angular-d61c6b21bad3 , powinniśmy wypisać się z .fromEvent()subskrypcji
Jon Onstott
153

Jeśli nie chcesz się tym zajmować @angular/forms, możesz po prostu użyć RxJS Subjectz powiązaniami zmian.

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);
    }
}

To wywołuje wykrywanie zmian. Aby poznać sposób, który nie wyzwala wykrywania zmian, zapoznaj się z odpowiedzią Marka.


Aktualizacja

.pipe(debounceTime(300), distinctUntilChanged()) jest potrzebny dla rxjs 6.

Przykład:

   constructor() {
        this.modelChanged.pipe(
            debounceTime(300), 
            distinctUntilChanged())
            .subscribe(model => this.model = model);
    }
0xcaff
źródło
5
Wolę to rozwiązanie! Pracowałem z Angular 2.0.0, RXJS 5.0.0-beta 12
alsco77
2
Działał doskonale, prosto i przejrzyście, bez formy. Jestem na Angular 4.1.3, rxjs 5.1.1
piąty
Myślę, że jest to lepsze rozwiązanie, ponieważ ma możliwość pracy z formularzami w razie potrzeby, ale usuwa tę zależność, dzięki czemu implementacja jest znacznie prostsza. Dzięki.
Max
2
.pipe(debounceTime(300), distinctUntilChanged())jest potrzebny do rxjs 6
Icycool
Rozwiązanie mnie uratowało. Używałem keyUpzdarzenie na input.nativeElementw sposób mat-table, który przestał działać, gdy liczba kolumn została zmieniona
igorepst
35

Mogłaby zostać wdrożona jako dyrektywa

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();
  }

}

użyj go jak

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

próbka składnika

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
źródło
1
z większą liczbą importów, które działały dla mnie: import "rxjs / add / operator / debounceTime"; import "rxjs / add / operator / anotherUntilChanged";
Sbl
2
To zdecydowanie ułatwia wdrożenie całej aplikacji
joshcomley
1
isFirstChange jest używany do nie emitowania podczas inicjalizacji
Oleg Polezky
2
Działa w Angular 8 i rxjs 6.5.2 z następującymi zmianami. Jeśli chcesz użyć składni potoku, zmień następujące ustawienia: import 'rxjs/add/operator/debounceTime'; import 'rxjs/add/operator/distinctUntilChanged';na import { debounceTime, distinctUntilChanged } from 'rxjs/operators';i this.model.valueChanges .debounceTime(this.debounceTime) .distinctUntilChanged()nathis.model.valueChanges .pipe( debounceTime(this.debounceTime), distinctUntilChanged() )
kumaheiyama
1
Działa w Angular 9 i rxjs 6.5.4 ze zmianami, które @kumaheiyama stwierdził w swoim komentarzu. Po prostu nie zapomnij wyeksportować dyrektywy w module, w którym ją tworzysz. I nie zapomnij dołączyć modułu, w którym tworzysz tę dyrektywę, do modułu, w którym go używasz.
Filip Savic
29

Ponieważ temat jest stary, większość odpowiedzi nie działa w Angular 6/7/8/9 i / lub używa innych bibliotek.
Oto krótkie i proste rozwiązanie dla Angular 6+ z RxJS.

Najpierw zaimportuj niezbędne rzeczy:

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

Zainicjuj dnia 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();
  }
}

Użyj w ten sposób:

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

PS: W przypadku bardziej złożonych i wydajnych rozwiązań możesz nadal chcieć sprawdzić inne odpowiedzi.

Just Shadow
źródło
1
Dlaczego nie zrezygnujesz z subskrypcji w Destroy?
Virendra Singh Rathore
Zaktualizowano. Dzięki, że zauważyłeś!
Just Shadow
1
@JustShadow Dziękuję! To było naprawdę pomocne.
Niral Munjariya
Działa to idealnie za pierwszym razem. Ale kiedy w jakiś sposób usuwam wyszukiwany tekst, odpowiedź na następne żądanie zajmuje zbyt dużo czasu.
Sadiksha Gautam
To jest dziwne. Po mojej stronie nadal działa dobrze. Czy możesz podać więcej informacji, a może otworzyć nowe pytanie?
Just Shadow,
28

Nie jest bezpośrednio dostępny, jak w angular1, ale możesz łatwo bawić się obserwowalnymi NgFormControl i RxJS:

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

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

Ten post na blogu wyjaśnia to jasno: http://blog.hardtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html

Tutaj jest to autouzupełnianie, ale działa we wszystkich scenariuszach.

bertrandg
źródło
ale jest błąd z serwisu, to nie działa ponownie
Arun Tyagi
Nie rozumiem przykładu. [...] jest jednokierunkowym wiązaniem celu. Dlaczego można zgłosić kontener valueChanges? nie powinno to być coś. jak (ngFormControl)="..."?
phil294
20

Możesz stworzyć Observable RxJS (v.6), który robi, co chcesz.

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
źródło
Dzięki, że pomogło, jednak myślę, że import powinien pochodzić z rsjs/Rx, miałem błędy podczas korzystania z importu tak, jak go napisałeś ... więc w moim przypadku jest to teraz:import { Observable } from 'rxjs/Rx';
ghiscoding
2
@ghiscoding To zależy od wersji rxjs. W wersji 6 to: import { Observable } from 'rxjs';.
Matthias
Dzięki! Na marginesie, możesz po prostu skorzystać z jednego pipepołączeniapipe(debounceTime(300), distinctUntilChanged())
al.
1
searchChangeObserver jest subskrybentem, więc searchChangeSubscriber będzie lepszą nazwą.
Khonsort
12

Dla osób korzystających lodash, to jest bardzo łatwe do nieczułości żadnej funkcji:

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

następnie wrzuć coś takiego do swojego szablonu:

<(input)="changed($event.target.value)" />
Brad C.
źródło
3
lub po prostu (input) = "change ($ event.target.value)"
Jamie Kudla
1
Dziękuję za odpowiedź lodash :)
Vamsi
Uważam, że to nadal będzie wyzwalać wykrywanie zmian kątowych przy każdej pojedynczej zmianie, niezależnie od debountingu.
AsGoodAsItGets
5

Rozwiązanie z inicjalizacją abonenta bezpośrednio w funkcji zdarzenia:

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);
    }
}

I html:

<input type="text" (input)="onFind($event.target.value)">
Serhii Vasko
źródło
Działa całkowicie dobrze dla kątowego 8-początkowego autouzupełniania pola tekstowego. Wielkie dzięki.
Jasmin Akther Suma
4

Rozwiązałem to, pisząc dekorator odbicia. Opisany problem można rozwiązać, stosując metodę @debounceAccessor do metody dostępu set właściwości.

Dostarczyłem również dodatkowy dekorator odbicia dla metod, który może być przydatny na inne okazje.

To sprawia, że ​​usunięcie właściwości lub metody jest bardzo łatwe. Parametr to liczba milisekund, które powinno trwać odbicie, 100 ms w przykładzie poniżej.

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


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

A oto kod dla dekoratorów:

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);
    }
  }
}

Dodałem dodatkowy parametr do dekoratora metody, który pozwala wyzwalać metodę PO opóźnieniu odbicia. Zrobiłem to, aby móc na przykład używać go w połączeniu ze zdarzeniami najechania kursorem myszy lub zmiany rozmiaru, gdzie chciałem, aby przechwytywanie miało miejsce na końcu strumienia zdarzeń. Jednak w tym przypadku metoda nie zwróci wartości.

Fredrik_Macrobond
źródło
3

Możemy stworzyć dyrektywę [debounce], która nadpisuje domyślną funkcję viewToModelUpdate ngModel pustą.

Kod dyrektywy

@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);
        });
    }
}

Jak tego użyć

<div class="ui input">
  <input debounce [delay]=500 [(ngModel)]="myData" type="text">
</div>
BebbaPig
źródło
2

Plik HTML:

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

Plik TS:

timer = null;
time = 250;
  search(searchStr : string) : void {
    clearTimeout(this.timer);
    this.timer = setTimeout(()=>{
      console.log(searchStr);
    }, time)
  }
Yoav Schniederman
źródło
2

Prostym rozwiązaniem byłoby utworzenie dyrektywy, którą można by zastosować do dowolnego elementu sterującego.

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);
                }
            });
    }
}

użycie byłoby

<textarea [ngModel]="somevalue"   
          [debounce]="2000"
          (onDebounce)="somevalue = $event"                               
          rows="3">
</textarea>
Ashg
źródło
Ta klasa jest daleka od wkompilowania Angular 7.
Stephane
1

Spędziłem nad tym wiele godzin, mam nadzieję, że uda mi się uratować trochę czasu komuś innemu. Dla mnie następujące podejście do używania debouncena kontrolce jest dla mnie bardziej intuicyjne i łatwiejsze do zrozumienia. Jest oparty na rozwiązaniu dokumentacyjnym angular.io do autouzupełniania, ale z możliwością przechwytywania połączeń bez konieczności wiązania danych z DOM.

Plunker

Scenariuszem użycia może być sprawdzenie nazwy użytkownika po jej wpisaniu, aby zobaczyć, czy ktoś już ją wykorzystał, a następnie ostrzeżenie użytkownika.

Uwaga: nie zapomnij, (blur)="function(something.value)może to mieć dla Ciebie więcej sensu w zależności od Twoich potrzeb.

Helzgate
źródło
1

DebounceTime w Angular 7 z RxJS v6

Link do źródła

Demo Link

wprowadź opis obrazu tutaj

W szablonie HTML

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

W komponencie

    ....
    ....
    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)});
    }

    }
Code Spy
źródło
1

Możesz również rozwiązać ten problem za pomocą dekoratorów, na przykład używając dekoratora debounce z utils-decorator lib ( npm install utils-decorators):

import {debounce} from 'utils-decorators';

class MyAppComponent {

  @debounce(500)
  firstNameChanged($event, first) {
   ...
  }
}
vlio20
źródło
0

To najlepsze rozwiązanie, jakie do tej pory znalazłem. Aktualizuje ngModelon bluridebounce

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);
        });
    }
}

zgodnie z wypożyczeniem z https://stackoverflow.com/a/47823960/3955513

Następnie w HTML:

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

Na blurmodelu jest wyraźnie zaktualizowane przy użyciu zwykłego JavaScript.

Przykład tutaj: https://stackblitz.com/edit/ng2-debounce-working

Shyamal Parikh
źródło