针对 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() {
// 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在下面的注释中所询问的那样。
尽管上面的代码可能是Angular的方式,但它效率不高。每次击键和每个调整大小事件(即使它们被取消和限制)都会导致更改检测运行。换句话说,取消抖动和限制不会影响更改检测运行的频率。(我发现Tobias Bosch的GitHub评论证实了这一点。当您运行 plunker 时,您可以看到这一点,并且当您在输入框中键入内容或调整窗口大小时,可以看到被调用的次数。(使用蓝色的“x”按钮在单独的窗口中运行 plunker 以查看调整大小事件。ngDoCheck()
一种更有效的技术是从Angular的“区域”之外的事件中自己创建RxJS Observables。这样,每次触发事件时都不会调用更改检测。然后,在订阅回调方法中,手动触发更改检测 , 即,您可以控制何时调用更改检测:
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 的评论中打了个电话。请注意,调用将导致被调用。ApplicationRef.tick()
tick()
ngDoCheck()