Angular 2+ 和去抖动 [英] Angular 2+ and debounce

查看:31
本文介绍了Angular 2+ 和去抖动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 AngularJS 中,我能够通过使用 ng-model 选项来消除模型的抖动.

In AngularJS I was able to debounce a model by using ng-model options.

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

如何在 Angular 中对模型进行去抖动?
我试图在文档中搜索去抖动,但找不到任何东西.

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

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

A solution would be to write my own debounce function, for example:

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

But I'm looking for a built in function, is there one in Angular?

推荐答案

针对 RC.5 更新

使用 Angular 2,我们可以在表单控件的 valueChanges observable 上使用 RxJS 运算符 debounceTime() 去抖动:

With Angular 2 we can debounce using RxJS operator debounceTime() on a form control's valueChanges observable:

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 在下面的评论中所问的那样.

The code above also includes an example of how to throttle window resize events, as asked by @albanx in a comment below.

虽然上面的代码可能是 Angular 的方式,但效率不高.每个击键和每个调整大小事件,即使它们被去抖动和限制,也会导致更改检测运行.换句话说,去抖动和限制不会影响更改检测运行的频率.(我发现了 Tobias Bosch 的 GitHub 评论 证实了这一点.)可以在运行 plunker 时看到这一点,并且可以看到在输入框中键入内容或调整窗口大小时调用了多少次 ngDoCheck().(使用蓝色的x"按钮在单独的窗口中运行 plunker 以查看调整大小事件.)

Although the above code is probably the Angular-way of doing it, it is not efficient. Every keystroke and every resize event, even though they are debounced and throttled, results in change detection running. In other words, debouncing and throttling do not affect how often change detection runs. (I found a GitHub comment by Tobias Bosch that confirms this.) You can see this when you run the plunker and you see how many times ngDoCheck() is being called when you type into the input box or resize the window. (Use the blue "x" button to run the plunker in a separate window to see the resize events.)

一种更有效的技术是从事件中自己创建 RxJS Observables,在 Angular 的区域"之外.这样,每次触发事件时都不会调用更改检测.然后,在您的订阅回调方法中,手动触发更改检测–即,您控制何时调用更改检测:

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.

I use ngAfterViewInit() instead of ngOnInit() to ensure that inputElRef is defined.

detectChanges() 将对该组件及其子组件运行更改检测.如果您更愿意从根组件运行更改检测(即运行完整的更改检测检查),请使用 ApplicationRef.tick() 代替.(我在 plunker 的注释中调用了 ApplicationRef.tick().)请注意,调用 tick() 将导致 ngDoCheck()被调用.

detectChanges() will run change detection on this component and its children. If you would rather run change detection from the root component (i.e., run a full change detection check) then use ApplicationRef.tick() instead. (I put a call to ApplicationRef.tick() in comments in the plunker.) Note that calling tick() will cause ngDoCheck() to be called.

这篇关于Angular 2+ 和去抖动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆