RC.5用に更新
Angular 2 debounceTime()
では、フォームコントロールのvalueChanges
オブザーバブルでRxJSオペレーターを使用してデバウンスできます。
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
上記のコードには、ウィンドウのサイズ変更イベントを抑制する方法の例も含まれています。
上記のコードはおそらくAngularの方法ですが、効率的ではありません。すべてのキーストロークとすべてのサイズ変更イベントは、それらがデバウンスおよびスロットルされていても、変更検出が実行されます。つまり、デバウンスとスロットルは、変更検出の実行頻度に影響を与えません。(これを確認するTobias BoschのGitHubコメントを見つけました。)これは、プランカーを実行すると表示ngDoCheck()
され、入力ボックスに入力するかウィンドウのサイズを変更すると、何回呼び出されたかがわかります。(青色の「x」ボタンを使用して別のウィンドウでプランカーを実行し、サイズ変更イベントを確認します。)
より効率的な手法は、Angularの「ゾーン」外のイベントからRxJS Observableを自分で作成することです。このように、イベントが発生するたびに変更検出が呼び出されることはありません。次に、サブスクライブコールバックメソッドで、手動で変更検出をトリガーします。つまり、変更検出がいつ呼び出されるかを制御します。
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()
代わりにを使用します。(私ApplicationRef.tick()
はplunkerのコメントに呼び出しを入れました。)呼び出しtick()
は呼び出されることngDoCheck()
に注意してください。