在AngularJS中,我可以通过使用ng模型选项来消除模型的抖动.

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

How can I debounce a model in Angular?
I tried to search for debounce in the docs but I couldn't find anything.

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

但我在寻找一个内置函数,有没有内置函数?

推荐答案

Updated for RC.5

对于Angular 2,我们可以在表单控件的valueChanges observable上使用RxJS运算符debounceTime()进行debounce :

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在下面的 comments 中所问,上面的代码还包括一个如何调节窗口大小事件的示例.


虽然上面的代码可能是完成这项工作的Angular 方式,但它的效率并不高.每一次击键和每一次调整大小事件,即使它们被取消并限制,都会导致运行更改检测.换句话说,是debouncing and throttling do not affect how often change detection runs.(我找到了托拜厄斯·博世(Tobias Bosch)的GitHub comment英镑,证实了这一点.)您可以在运行柱塞时看到这一点,当您在输入框中键入内容或调整窗口大小时,您可以看到ngDoCheck()被调用了多少次.(使用蓝色"x"按钮在单独的窗口中运行柱塞以查看调整大小事件.)

A more efficient technique is to create RxJS Observables yourself from the events, outside of Angular's "zone". This way, change detection is not called each time an event fires. Then, in your subscribe callback methods, manually trigger change detection – i.e., you control when change detection is called:

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().(我在plunker的 comments 中打电话给ApplicationRef.tick().)注意,呼叫tick()将导致呼叫ngDoCheck().

Angular相关问答推荐

如何在构造函数中测试MatDialog.Open

无法绑定到appendTo,因为它不是p-confirmPopup的已知属性

Angular 17:延迟加载带参数的独立组件?

使用BLOB数据类型时,HTTPCLIENT中可能存在错误

Angular 16模块中未使用的组件是否从Bundle 包中删除(树摇动)?

崩溃Angular项目Docker容器

如何在Angular功能路由保护中取消订阅RxJs主题

Angular APP_INITIALIZER 中的 MSAL 身份验证

BehaviorSubject 在 Angular 中制作数据之间的时间表(或计时器)

Angular 14 类型表单 - 数字控件的初始值

RxJS 基于先前的数据响应执行请求(涉及循环对象数组)

无法将项目从 Angular 13 更新到 14

如何在 Angular4 中访问组件的 nativeElement?

取消订阅服务中的http observable

初始化所有子类后的Angular 2生命周期钩子是什么?

使用 Jasmine 和 Karma 对 Angular 进行单元测试,错误:无法绑定到xxx,因为它不是xxxxxx的已知属性.

unexpected token < 错误

RxJS:takeUntil() Angular 组件的 ngOnDestroy()

无法绑定到 target,因为它不是div的已知属性

此类通过 SomeModule -> SomeComponent 对消费者可见,但不会从顶级库入口点导出