[解決済み] Angular 2+とデバウンス
質問
AngularJSで、ng-modelのオプションを使ってモデルをデバウンスすることができました。
ng-model-options="{ debounce: 1000 }"
Angularでモデルをデバウンスするには?
ドキュメントでdebounceを検索してみましたが、何も見つかりませんでした。
https://angular.io/search/#stq=debounce&stp=1
解決策としては、例えばデバウンス機能を自作することでしょうか。
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にはあるのでしょうか?
どのように解決するのですか?
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();
}
}
上のコードには、下のコメントで @albanx が質問した、ウィンドウのリサイズイベントをスロットルする方法の例も含まれています。
上記のコードはおそらくAngular流のやり方だと思いますが、効率的ではありません。 すべてのキーストロークとすべてのリサイズイベントは、デバウンスされスロットルされているにもかかわらず、結果的に変更検出が実行されることになります。 言い換えれば
デバウンスとスロットリングは、変更検知の実行頻度に影響しません。
. (を発見しました)。
GitHubコメント
Tobias Boschによるもので、これを確認しました)。 これはプランカーを実行するとわかります。
ngDoCheck()
は、入力ボックスに文字を入力したときやウィンドウのサイズを変更したときに呼び出されます。(青い "x" ボタンを使って、plunker を別のウィンドウで実行し、リサイズイベントを見ることができます。)
より効率的なテクニックは、Angularの"zone"の外側で、イベントからRxJS Observablesを自分で作成することです。 この方法では、イベントが発生するたびに変更検出が呼び出されることはありません。そして、subscribeコールバックメソッドで、手動で変更検知をトリガーします。つまり、変更検知がいつ呼び出されるかをコントロールします。
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();
}
}
私が使っているのは
ngAfterViewInit()
の代わりに
ngOnInit()
を確保するために
inputElRef
が定義されています。
detectChanges()
は、このコンポーネントとその子コンポーネントに対して変更検知を実行します。もし、ルートコンポーネントから変更検知を行いたい場合 (つまり、完全な変更検知チェックを行いたい場合) は、次のようにしてください。
ApplicationRef.tick()
の代わりに (私は呼び出しを
ApplicationRef.tick()
をプランカー内のコメントで表示しています)。を呼び出すことに注意してください。
tick()
は
ngDoCheck()
が呼び出されます。
関連
-
[解決済み】JavaScriptで関数が存在するかどうかを確認する方法は?
-
[解決済み] JavaScriptで "use strict "は何をするのか、その根拠は?
-
[解決済み] let "と "var "の使い分けは?
-
[解決済み] callとapplyの違いは何ですか?
-
[解決済み] URLを新しいタブで開く(新しいウィンドウではない)
-
[解決済み] JSONPとは何か、なぜ作られたのか?
-
[解決済み] angular-routeとangular-ui-routerの違いは何ですか?
-
[解決済み] Angular HTMLバインディング
-
[解決済み] Angular:*ngClassを使った条件付きクラス
-
[解決済み】PromiseとObservablesの違いは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】TypeError: $(...).DataTable は関数ではありません。
-
[解決済み】Failed to load resource: net::ERR_FILE_NOT_FOUND loading json.js
-
[解決済み】SecurityError: オリジンを持つフレームがクロスオリジンフレームにアクセスするのをブロックした
-
[解決済み】Angular JS Uncaught Error。[インジェクター:モジュラー]。
-
[解決済み] Uncaught Invariant Violation: 前のレンダリング中よりも多くのフックをレンダリングした
-
[解決済み] 解決済み】clearInterval()が動作しない [重複] [重複]
-
[解決済み】XMLHttpRequestモジュールが定義されていない/見つからない
-
[解決済み】JavaScriptのinnerHTMLで要素が更新されない
-
[解決済み】最大呼び出しスタックサイズ超過エラーとその修正方法とは?
-
[解決済み] テスト