Angolare e rimbalzare


160

In AngularJS posso rimbalzare un modello usando le opzioni ng-model.

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

Come posso rimbalzare un modello in angolare? Ho provato a cercare il rimbalzo nei documenti ma non sono riuscito a trovare nulla.

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

Una soluzione sarebbe quella di scrivere la mia funzione di debounce, ad esempio:

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

E il mio HTML

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

Ma sto cercando una funzione build in, ce n'è una in Angular?


3
Questo potrebbe essere rilevante github.com/angular/angular/issues/1773 , non apparentemente ancora impiantato.
Eric Martinez,

Controlla questo post per Angular 7 con RxJS v6 freakyjolly.com/…
Codice spia

Risposte:


202

Aggiornato per RC.5

Con Angular 2 possiamo rimbalzare usando l'operatore RxJS sull'osservabile di debounceTime()un form control valueChanges:

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

Il codice sopra include anche un esempio di come limitare gli eventi di ridimensionamento della finestra, come richiesto da @albanx in un commento qui sotto.


Sebbene il codice sopra sia probabilmente il modo angolare di farlo, non è efficiente. Ogni sequenza di tasti e ogni evento di ridimensionamento, anche se sono rimbalzati e limitati, provoca il rilevamento delle modifiche in esecuzione. In altre parole, il debouncing e il throttling non influiscono sulla frequenza con cui viene eseguito il rilevamento delle modifiche . (Ho trovato un commento GitHub di Tobias Bosch che lo conferma.) Puoi vederlo quando esegui il plunker e vedi quante volte ngDoCheck()viene chiamato quando digiti nella casella di input o ridimensioni la finestra. (Usa il pulsante blu "x" per eseguire il plunker in una finestra separata per vedere gli eventi di ridimensionamento.)

Una tecnica più efficiente è quella di creare osservabili RxJS dagli eventi, al di fuori della "zona" di Angular. In questo modo, il rilevamento delle modifiche non viene chiamato ogni volta che viene generato un evento. Quindi, nei metodi di callback di sottoscrizione, attiva manualmente il rilevamento delle modifiche, ovvero controlli quando viene chiamato il rilevamento delle modifiche:

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

Uso ngAfterViewInit()invece di ngOnInit()assicurarsi che inputElRefsia definito.

detectChanges()eseguirà il rilevamento delle modifiche su questo componente e sui relativi figli. Se preferisci eseguire il rilevamento delle modifiche dal componente radice (ovvero, eseguire un controllo completo del rilevamento delle modifiche), utilizza ApplicationRef.tick()invece. (Ho inserito una chiamata ApplicationRef.tick()nei commenti nel plunker.) Nota che la chiamata tick()verrà ngDoCheck()chiamata.


2
@Mark Rajcok Penso che invece di [valore], dovresti usare [ngModel], perché [valore] non aggiorna il valore di input.
Milad,

1
esiste un metodo di debounce generico (ad esempio da applicare all'evento di ridimensionamento della finestra)?
albanx,

1
@MarkRajcok Credo che il problema con il CD che hai descritto nella tua risposta sia stato risolto da github.com/angular/zone.js/pull/843
Jefftopia,

2
Quando dovremmo annullare l'iscrizione per evitare perdite di memoria?
calunnia il

1
@slanden Sì, secondo netbasal.com/when-to-unsubscribe-in-angular-d61c6b21bad3 , dovremmo annullare l'iscrizione agli .fromEvent()abbonamenti
Jon Onstott,

153

Se non vuoi occuparti di @angular/forms, puoi semplicemente usare un RxJS Subjectcon collegamenti di modifica.

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

Ciò attiva il rilevamento delle modifiche. Per un modo che non attiva il rilevamento delle modifiche, controlla la risposta di Mark.


Aggiornare

.pipe(debounceTime(300), distinctUntilChanged()) è necessario per rxjs 6.

Esempio:

   constructor() {
        this.modelChanged.pipe(
            debounceTime(300), 
            distinctUntilChanged())
            .subscribe(model => this.model = model);
    }

5
Preferisco questa soluzione! Ha funzionato con angular 2.0.0, rxjs 5.0.0-beta 12
alsco77,

2
Ha funzionato perfettamente, semplice e chiaro, nessuna forma coinvolta. Sono su Angular 4.1.3, rxjs 5.1.1
quinto

Penso che questa sia una soluzione superiore poiché ha la possibilità di lavorare con i moduli se necessario, ma rimuove tale dipendenza rendendo l'implementazione molto più semplice. Grazie.
Max

2
.pipe(debounceTime(300), distinctUntilChanged())è necessario per rxjs 6
Icycool il

La soluzione mi ha salvato. Stavo usando keyUpevento input.nativeElementin una mat-table, che ha smesso di funzionare quando il numero di colonne è stato cambiato
igorepst

35

Potrebbe essere attuato come direttiva

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

}

usalo come

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

campione componente

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

1
con più importazioni, che ha funzionato per me: import "rxjs / add / operator / debounceTime"; import "rxjs / add / operator / distinctUntilChanged";
Sbl

2
Ciò rende di gran lunga la più semplice implementazione di un'applicazione estesa
joshcomley

1
isFirstChange non viene utilizzato per l'inizializzazione
Oleg Polezky,

2
Funziona in Angular 8 e rxjs 6.5.2 con le seguenti modifiche. Se si desidera utilizzare la sintassi della pipe, modificare quanto segue: import 'rxjs/add/operator/debounceTime'; import 'rxjs/add/operator/distinctUntilChanged';to import { debounceTime, distinctUntilChanged } from 'rxjs/operators';e this.model.valueChanges .debounceTime(this.debounceTime) .distinctUntilChanged()tothis.model.valueChanges .pipe( debounceTime(this.debounceTime), distinctUntilChanged() )
kumaheiyama

1
Funziona in Angular 9 e rxjs 6.5.4 con le modifiche che @kumaheiyama ha affermato nel suo commento. Non dimenticare di esportare la direttiva nel modulo in cui la stai creando. E non dimenticare di includere il modulo in cui stai creando questa direttiva, nel modulo in cui lo stai utilizzando.
Filip Savic,

29

Poiché l'argomento è vecchio, la maggior parte delle risposte non funziona su Angular 6/7/8/9 e / o usa altre librerie.
Ecco quindi una soluzione breve e semplice per Angular 6+ con RxJS.

Importa prima le cose necessarie:

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

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

Usa in questo modo:

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

PS: per soluzioni più complesse ed efficienti potresti voler controllare altre risposte.


1
Perché non annullare l'iscrizione a distruggere?
Virendra Singh Rathore,

Aggiornato. Grazie per averlo notato!
Solo Shadow

1
@JustShadow Grazie! È stato davvero utile
Niral Munjariya,

Funziona perfettamente al primo tentativo. Ma quando elimino il testo cercato in qualche modo, la prossima richiesta impiega troppo tempo a rispondere.
Sadiksha Gautam,

È strano. Funziona ancora bene dalla mia parte. Potresti condividere maggiori informazioni o magari aprire una nuova domanda per questo?
Solo Shadow

28

Non direttamente accessibile come in angular1 ma puoi giocare facilmente con gli osservabili NgFormControl e RxJS:

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

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

Questo post sul blog lo spiega chiaramente: http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html

Qui è per un completamento automatico ma funziona in tutti gli scenari.


ma c'è un errore dal servizio, questo non è più in esecuzione
Arun Tyagi,

Non capisco l'esempio. [...] è un'associazione target unidirezionale. Perché il contenitore può essere avvisato valueChanges? non dovrebbe essere sth. come (ngFormControl)="..."?
phil294,

20

È possibile creare un RxJS (v.6) osservabile che fa quello che vuoi.

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


}

Grazie che mi ha aiutato, comunque penso che l'importazione dovrebbe essere rsjs/Rx, ho avuto degli errori quando ho usato l'importazione nel modo in cui l'hai scritta ... quindi nel mio caso è adesso:import { Observable } from 'rxjs/Rx';
ghiscoding del

2
@ghiscoding Dipende dalla versione di rxjs. Nella versione 6 è: import { Observable } from 'rxjs';.
Matthias,

Grazie! Per inciso, puoi usare solo una pipechiamatapipe(debounceTime(300), distinctUntilChanged())
al.

1
searchChangeObserver è un Sottoscrittore, quindi searchChangeSubscriber sarà un nome migliore.
Khonsort,

12

Per chiunque usi lodash, è estremamente facile rimbalzare qualsiasi funzione:

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

quindi lancia qualcosa di simile nel tuo modello:

<(input)="changed($event.target.value)" />

3
or just (input) = "changed ($ event.target.value)"
Jamie Kudla,

1
Grazie per aver risposto con lodash :)
Vamsi il

Credo che questo attiverà comunque il rilevamento del cambio angolare su ogni singolo cambiamento, indipendentemente dal rimbalzo.
AsGoodAsIt Ottiene il

5

Soluzione con l'abbonamento di inizializzazione direttamente nella funzione evento:

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

E HTML:

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

Funziona perfettamente con la casella di testo angolare a completamento automatico. Molte grazie.
Jasmin Akther Suma,

4

Ho risolto questo problema scrivendo un decoratore debounce. Il problema descritto potrebbe essere risolto applicando @debounceAccessor all'accessorio set della proprietà.

Ho anche fornito un decoratore di debounce aggiuntivo per i metodi, che può essere utile per altre occasioni.

Ciò semplifica il debounce di una proprietà o di un metodo. Il parametro è il numero di millisecondi che dovrebbe durare il rimbalzo, 100 ms nell'esempio seguente.

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


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

Ed ecco il codice per i decoratori:

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

Ho aggiunto un parametro aggiuntivo per il decoratore del metodo che consente di attivare il metodo DOPO il ritardo di rimbalzo. L'ho fatto in modo da poterlo ad esempio utilizzare in combinazione con il passaggio del mouse o il ridimensionamento degli eventi, dove volevo che la cattura avvenisse alla fine del flusso di eventi. In questo caso, tuttavia, il metodo non restituirà un valore.


3

Possiamo creare una direttiva [debounce] che sovrascrive la funzione viewToModelUpdate predefinita di ngModel con una vuota.

Codice Direttiva

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

Come usarlo

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

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

2

Una soluzione semplice sarebbe quella di creare una direttiva che puoi applicare a qualsiasi controllo.

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

l'uso sarebbe

<textarea [ngModel]="somevalue"   
          [debounce]="2000"
          (onDebounce)="somevalue = $event"                               
          rows="3">
</textarea>

Questa classe è lungi dall'essere compilata Angular 7.
Stephane,

1

Ho passato ore su questo, spero di poter salvare qualcun altro un po 'di tempo. Per me il seguente approccio all'uso di debounceun controllo è più intuitivo e più facile da capire per me. È basato sulla soluzione di documenti angular.io per il completamento automatico, ma con la possibilità per me di intercettare le chiamate senza dover dipendere dal collegamento dei dati al DOM.

Plunker

Uno scenario d'uso per questo potrebbe essere il controllo di un nome utente dopo che è stato digitato per vedere se qualcuno lo ha già preso, quindi avvisare l'utente.

Nota: non dimenticare, (blur)="function(something.value)potrebbe avere più senso per te a seconda delle tue esigenze.


1

DebounceTime in Angular 7 con RxJS v6

Link alla fonte

demo link

inserisci qui la descrizione dell'immagine

Nel modello HTML

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

Nel componente

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

    }

1

Puoi anche risolverlo usando un decoratore, Per esempio usando il decoratore debounce da utils-decorator lib ( npm install utils-decorators):

import {debounce} from 'utils-decorators';

class MyAppComponent {

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

0

Questa è la migliore soluzione che ho trovato fino ad ora. Aggiorna ngModelon bluredebounce

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

come preso in prestito da https://stackoverflow.com/a/47823960/3955513

Quindi in HTML:

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

Sul blurmodello viene esplicitamente aggiornato utilizzando javascript semplice.

Esempio qui: https://stackblitz.com/edit/ng2-debounce-working

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.