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