已更新至 RC.5 版本
在 Angular 2 中,我们可以使用 RxJS 操作符 debounceTime()
对表单控件的 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() {
this.formCtrlSub = this.firstNameControl.valueChanges
.debounceTime(1000)
.subscribe(newValue => this.firstName = newValue);
this.resizeSub = Observable.fromEvent(window, 'resize')
.throttleTime(200)
.subscribe(e => {
console.log('resize event', e);
this.firstName += '*';
});
}
ngDoCheck() { console.log('change detection'); }
ngOnDestroy() {
this.formCtrlSub.unsubscribe();
this.resizeSub .unsubscribe();
}
}
Plunker
上述代码还包括了如何限制窗口调整事件的示例,正如@albanx在下面的评论中提到的。
虽然上述代码可能是Angular的做法,但并不高效。即使进行防抖和节流,每个按键输入和每个调整事件都会触发变更检测。换句话说,防抖和节流不会影响变更检测运行的频率。(我在Tobias Bosch的GitHub评论中找到了这个确认。)当您运行Plunker时,可以看到在您输入内容或调整窗口大小时ngDoCheck()
被调用的次数。 (使用蓝色的“x”按钮在单独的窗口中运行Plunker,以查看调整事件。)
一种更有效的技术是从事件中自己创建RxJS Observables,而不是在Angular的“zone”之外。 这样,每次事件触发时都不会调用变更检测。 然后,在您的订阅回调方法中,手动触发变更检测,即控制何时调用变更检测:
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 += '*';
this.cdref.detectChanges();
});
});
}
ngDoCheck() { console.log('cd'); }
ngOnDestroy() {
this.keyupSub .unsubscribe();
this.resizeSub.unsubscribe();
}
}
Plunker
我使用ngAfterViewInit()
代替ngOnInit()
,以确保inputElRef
被定义。
detectChanges()
会在该组件及其子组件上运行变更检测。如果您希望从根组件开始运行变更检测(即运行完整的变更检测),则应改用ApplicationRef.tick()
。 (我在plunker中将ApplicationRef.tick()
调用放在了注释中。)请注意,调用tick()
将导致调用ngDoCheck()
。