在AngularJS中,我可以使用ng-model选项来撤销一个模型。

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

如何在Angular中释放一个模型? 我试着在文档里搜索debounce,但什么都没找到。

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

一个解决方案是编写我自己的debounce函数,例如:

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中有吗?


当前回答

你可以创建一个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) {
      new Observable(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);
  }  


}

其他回答

我通过编写一个debounce decorator来解决这个问题。所描述的问题可以通过将@debounceAccessor应用于属性的集访问器来解决。

我还为方法提供了一个额外的debounce装饰器,它可以在其他场合使用。

这使得撤消属性或方法变得非常容易。该参数是反弹应该持续的毫秒数,在下面的示例中为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);
    }
  }
}

我为方法装饰器添加了一个额外的参数,让你在debounce延迟后触发方法。我这样做是为了在与鼠标悬停或调整事件相结合时使用它,我希望捕获发生在事件流的末尾。但是,在这种情况下,该方法不会返回值。

对于任何使用lodash的人来说,撤销任何函数都非常容易:

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

然后在模板中添加如下内容:

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

如果你不想处理@angular/forms,你可以只使用RxJS Subject和变更绑定。

view.component.html

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

view.component.ts

import { Subject } from 'rxjs';
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);
    }
}

这确实触发了变更检测。关于不触发变更检测的方法,请参阅Mark的回答。


更新

rxjs 6需要.pipe(debounceTime(300), distinctUntilChanged())。

例子:

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

你也可以通过使用装饰器来解决这个问题,例如通过使用utils-decorator lib中的debounce装饰器(npm install utils-decorators):

import {debounce} from 'utils-decorators';

class MyAppComponent {

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

由于这个话题已经很老了,大多数答案在Angular 6-13和/或使用其他库中都不起作用。 这里有一个简短的RxJS Angular 6+解决方案。

首先导入必要的东西:

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

实现ngOnInit和ngOnDestroy:

export class MyComponent implements OnInit, OnDestroy {
  public notesText: string;
  public 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)' />

附注:对于更复杂和有效的解决方案,您可能仍然需要检查其他答案。