Кутовий і дебютаційний


160

У AngularJS я можу зняти модель за допомогою параметрів ng-моделі.

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

Як я можу дебютувати модель у Angular? Я намагався шукати дебютацію в документах, але нічого не міг знайти.

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

Рішенням може бути написання власної функції дебютації, наприклад:

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

І мій html

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

Але я шукаю функцію побудови, чи є така у Angular?


3
Це може бути доречно github.com/angular/angular/isissue/1773 , але ще не представляється реалізованим.
Ерік Мартінес

Перевірте цю публікацію на Angular 7 з RxJS v6 freakyjolly.com/…
Code Spy

Відповіді:


202

Оновлено для RC.5

За допомогою Angular 2 ми можемо відмовитись debounceTime()від використання оператора RxJS на контролі форми, який можна 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

Код, наведений вище, також містить приклад того, як можна зменшити розмір вікна, змінивши розмір подій, про що @albanx запитав у коментарі нижче.


Хоча наведений вище код є, мабуть, кутовим способом його виконання, він не є ефективним. Кожне натискання клавіш і кожна подія зміни розміру, навіть якщо вони знімаються та утримуються, призводить до виявлення змін. Іншими словами, розмикання та дроселювання не впливають на те, як часто змінюються функції виявлення . (Я знайшов коментар GitHub від Tobias Bosch, який підтверджує це.) Ви можете це бачити, коли запускаєте планкер, і ви бачите, скільки разів ngDoCheck()викликається, коли ви вводите в поле введення або змінюєте розмір вікна. (Використовуйте синю кнопку "x", щоб запустити планкер в окремому вікні, щоб побачити події зміни розміру.)

Більш ефективним прийомом є створення RxJS Спостереження за подіями, поза межами "зони" Angular. Таким чином, виявлення змін не викликається щоразу, коли подія запускається. Потім у ваших методах зворотного виклику підписки вручну запускайте виявлення змін - тобто ви керуєте, коли викликається виявлення змін:

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

Я використовую ngAfterViewInit()замість того, ngOnInit()щоб переконатися, що inputElRefвизначено.

detectChanges()запустить виявлення змін цього компонента та його дочірніх елементів. Якщо ви бажаєте запустити виявлення змін з кореневого компонента (тобто, запустіть повну перевірку виявлення змін), тоді використовуйте ApplicationRef.tick()натомість. (Я подзвонив ApplicationRef.tick()у коментарях у планку.) Зауважте, що дзвінок tick()викликає дзвінок ngDoCheck().


2
@Mark Rajcok Я думаю, що замість [value] слід використовувати [ngModel], оскільки [value] не оновлює вхідне значення.
Мілад

1
чи є якийсь загальний метод дебютування (наприклад, застосувати до події зміни розміру вікна)?
albanx

1
@MarkRajcok Я вважаю, що проблема CD, яку ви описали у своїй відповіді, вирішена github.com/angular/zone.js/pull/843
Jefftopia

2
Коли нам потрібно скасувати підписку, щоб запобігти витоку пам'яті?
slanden

1
@slanden Так, згідно netbasal.com/when-to-unsubscribe-in-angular-d61c6b21bad3 , нам слід скасувати .fromEvent()підписку
Jon Onstott

153

Якщо ви не хочете мати справу з ними @angular/forms, ви можете просто використовувати RxJS Subjectзі зміною прив'язки.

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

Це робить виявлення змін тригера. Щоб не викликати виявлення змін, ознайомтеся з відповіддю Марка.


Оновлення

.pipe(debounceTime(300), distinctUntilChanged()) потрібен для rxjs 6.

Приклад:

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

5
Я вважаю за краще це рішення! Працював з кутовим 2.0.0, rxjs 5.0.0-beta 12
alsco77

2
Працював ідеально, просто і ясно, без форми. Я на Angular 4.1.3, rxjs 5.1.1
п’ятий

Я думаю, що це найкраще рішення, оскільки він має можливість працювати з формами, якщо це необхідно, але усуває цю залежність, що робить реалізацію набагато простішою. Дякую.
Макс

2
.pipe(debounceTime(300), distinctUntilChanged())потрібен rxjs 6
Icycool

Рішення врятувало мене. Я використовував keyUpподію на input.nativeElementв mat-table, який перестав працювати, коли кількість стовпців було змінено
igorepst

35

Це може бути реалізовано як Директива

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

}

використовувати його як

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

компонентний зразок

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
з більшим імпортом, який працював на мене: import "rxjs / add / operator / debounceTime"; імпортувати "rxjs / add / operator / distctUntilChanged";
Сбл

2
Це, безумовно, робить найпростішим у застосуванні широкий
приклад

1
isFirstChange використовується, щоб не випромінювати при ініціалізації
Олег Полезький

2
Працює в Angular 8 і rxjs 6.5.2 із наступними змінами. Якщо ви хочете використовувати синтаксис труби, змініть наступне: import 'rxjs/add/operator/debounceTime'; import 'rxjs/add/operator/distinctUntilChanged';до import { debounceTime, distinctUntilChanged } from 'rxjs/operators';і this.model.valueChanges .debounceTime(this.debounceTime) .distinctUntilChanged()вthis.model.valueChanges .pipe( debounceTime(this.debounceTime), distinctUntilChanged() )
kumaheiyama

1
Працює в Angular 9 і rxjs 6.5.4 із змінами @kumaheiyama, зазначеними у своєму коментарі. Просто не забудьте експортувати директиву в модуль, де ви її створюєте. І не забудьте включити модуль, для якого ви створюєте цю директиву, до модуля, де ви її використовуєте.
Філіп Савич

29

Оскільки тема давня, більшість відповідей не працює на Angular 6/7/8/9 та / або використовує інші лібри.
Отож ось коротке і просте рішення для Angular 6+ з RxJS.

Спочатку імпортуйте необхідні речі:

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

Ініціалізуйте 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();
  }
}

Використовуйте цей спосіб:

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

PS: Для більш складних та ефективних рішень ви все ще можете перевірити інші відповіді.


1
Чому ви не скасуєте підписку на знищення?
Virendra Singh

Оновлено. Дякуємо, що помітили!
Тільки Тінь

1
@JustShadow Дякую! Це було дуже корисно.
Niral Munjariya

Це чудово працює з першої спроби. Але коли я якось видаляю текст, що шукається, наступний запит потребує занадто багато часу, щоб відповісти.
Садікша Гаутам

Це дивно. На моєму боці це все добре працює. Не могли б ви поділитися інформацією чи, можливо, відкрити для цього нове запитання?
Just Shadow

28

Не доступний безпосередньо, як у angular1, але ви можете легко грати з NgFormControl та RxJS-спостереженнями:

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

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

Цей пост у блозі пояснює це чітко: http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html

Ось це для автозаповнення, але він працює у всіх сценаріях.


але в сервісі є помилка, це знову не працює
Арун Тягі

Я не розумію приклад. [...] є однобічним цільовим зв'язуванням. Чому про контейнер можна повідомити valueChanges? не повинно бути що-небудь. як (ngFormControl)="..."?
phil294

20

Ви можете створити в RxJS (V.6) Observable що робить все , що вам подобається.

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


}

Дякую, що допомогло, однак я думаю, що імпорт повинен бути з rsjs/Rx, у мене були помилки при використанні імпорту так, як ви це написали ... так що в моєму випадку це зараз:import { Observable } from 'rxjs/Rx';
ghiscoding

2
@ghiscoding Це залежить від версії rxjs. У 6 -й версії це: import { Observable } from 'rxjs';.
Маттіас

Дякую! Як сторону, ви можете використовувати лише один pipeдзвінокpipe(debounceTime(300), distinctUntilChanged())
al.

1
searchChangeObserver - підписник, тому searchChangeSubscriber стане кращим іменем.
Хонсорт

12

Для кожного, хто використовує lodash, надзвичайно легко дебютувати будь-яку функцію:

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

то просто киньте щось подібне у свій шаблон:

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

3
або просто (вхід) = "змінено ($ event.target.value)"
Джеймі Кудла,

1
Дякую за відповідь з lodash :)
Vamsi

Я вважаю, що це все ще спровокує виявлення кутових змін при кожній зміні, незалежно від деблокування.
AsGoodAsItGets

5

Рішення з ініціалізацією абонента безпосередньо у функції подій:

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

І html:

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

Працює цілком чудово для кутового 8 основного автозаповнення текстового поля. Дуже дякую.
Жасмін Актер Сума

4

Я вирішив це, написавши декоратора дебюту. Описану проблему можна вирішити, застосувавши @debounceAccessor до встановленого аксесуара властивості.

Я також поставив додатковий декоратор для дебютації методів, який може бути корисним для інших випадків.

Це дозволяє дуже легко дебютувати властивість чи метод. Параметр - кількість мілісекунд, яке повинне тривати, 100 мс у наведеному нижче прикладі.

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


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

А ось код для декораторів:

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

Я додав додатковий параметр для декоратора методу, який дозволить вам запустити метод ПІСЛЯ затримки дебютування. Я зробив це, щоб я міг, наприклад, використовувати його в поєднанні з переходом миші або зміною розміру подій, де я хотів, щоб захоплення відбулося в кінці потоку подій. Однак у цьому випадку метод не поверне значення.


3

Ми можемо створити директиву [debounce], яка замінює функцію viewgToModelUpdate за замовчуванням ngModel на порожню.

Кодекс директиви

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

Як ним користуватися

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

2

HTML-файл:

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

Файл TS:

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

2

Простим рішенням буде створення директиви, яку можна застосувати до будь-якого контролю.

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

використання буде

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

Цей клас далеко не збірний Angular 7.
Стефан

1

На це витратили години, сподіваюся, я можу врятувати когось іншого на деякий час. Для мене наступний підхід до використання debounceна контролі для мене більш інтуїтивний і легший для розуміння. Він побудований на рішенні angular.io docs для автозаповнення, але з можливістю перехоплювати дзвінки без необхідності залежати від прив’язки даних до DOM.

Плункер

Сценарій використання випадку для цього може бути перевірка імені користувача після його введення, щоб побачити, чи хтось його вже прийняв, а потім попередити користувача.

Примітка: не забувайте, це (blur)="function(something.value)може мати більше сенсу для вас залежно від ваших потреб.


1

Час дебютації в кутовій формі 7 з RxJS v6

Посилання на джерело

Демонстраційна посилання

введіть тут опис зображення

У шаблоні HTML

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

У складі

    ....
    ....
    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

Ви також можете вирішити це за допомогою декораторів, Наприклад, за допомогою декоратора дебютування з litil utils-decorator ( npm install utils-decorators):

import {debounce} from 'utils-decorators';

class MyAppComponent {

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

0

Це найкраще рішення, яке я знайшов досі. Оновлення ngModelна blurіdebounce

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

як запозичено з https://stackoverflow.com/a/47823960/3955513

Потім у HTML:

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

На blurмоделі явно оновлюється за допомогою простого javascript.

Приклад тут: https://stackblitz.com/edit/ng2-debounce-working

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.