click
のコンポーネントがあります。
<my-box (click)="openModal()"></my-box>
この要素をクリックすると、openModal
関数が実行されます。そして、複数のモーダルを開くのを防ぐために、1000msのスロットル時間を与えたいと思います。
私の最初のアプローチはSubject
を使用することでした(rxJsから)
//html
<my-box (click)="someSubject$.next()"></my-box>
//ts
public someSubject$:Subject<any> = new Subject();
...etc subscribe
しかし、それは少し冗長だと感じています。
次のアプローチではdirective
を使用していました。グーグルで見つけたコードを少し修正しました。
//ts
import {Directive, HostListener} from '@angular/core';
@Directive({
selector: '[noDoubleClick]'
})
export class PreventDoubleClickDirective {
constructor() {
}
@HostListener('click', ['$event'])
clickEvent(event) {
event.stopPropagation(); // not working as I expected.
event.preventDefault(); // not working as I expected.
event.srcElement.setAttribute('disabled', true); // it won't be working unless the element is input.
event.srcElement.setAttribute('style', 'pointer-events: none;'); // test if 'pointer-events: none' is working but seems not.
setTimeout(function () {
event.srcElement.removeAttribute('disabled');
}, 500);
}
}
//html
<my-box noDoubleClick (click)="openModal()"></my-box>
しかし、私が何をしようとも、常にopenModal
が実行されました。ディレクティブでopenModal
の実行を停止する方法が見つかりませんでした。
私はちょうど作ることができます
//ts
//In the openModal method.
openModal() {
public isClickable = true
setTimeout(() => {
this.newsClickable = true;
}, 1000);
...
}
しかし、再利用可能なコードについては、ディレクティブの使用が理想的だと思います。
どうすれば作成できますか?
RxJの debounce または debounceTime 演算子を使用して、ダブルクリックを防ぐことができます。 ここ は、カスタムのデバウンスクリックディレクティブを作成する方法に関する投稿でもあります。
投稿が将来削除される場合の最終的なコードは次のとおりです。
import { Directive, EventEmitter, HostListener, Input, OnDestroy, OnInit,
Output } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import { debounceTime } from 'rxjs/operators';
@Directive({
selector: '[appDebounceClick]'
})
export class DebounceClickDirective implements OnInit, OnDestroy {
@Input()
debounceTime = 500;
@Output()
debounceClick = new EventEmitter();
private clicks = new Subject();
private subscription: Subscription;
constructor() { }
ngOnInit() {
this.subscription = this.clicks.pipe(
debounceTime(this.debounceTime)
).subscribe(e => this.debounceClick.emit(e));
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
@HostListener('click', ['$event'])
clickEvent(event) {
event.preventDefault();
event.stopPropagation();
this.clicks.next(event);
}
}
<button appDebounceClick (debounceClick)="log()" [debounceTime]="700">Debounced Click</button>
私の場合、デバウンスの代わりにthrottleTime
がより良い解決策でした(イベントをすぐに起動し、時間が経過するまでブロックします)
一部の人々はthrottleTime
ディレクティブを要求したので、以下に追加します。 debounceTime
がラストクリックを待ってから実際のクリックイベントを発生させるため、このルートを選択しました。 throttleTime
は、その時間になるまでクリッカーがボタンを再度クリックすることを許可せず、代わりにクリックイベントをすぐに発生させます。
import { Directive, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { Subject, Subscription } from 'rxjs';
import { throttleTime } from 'rxjs/operators';
@Directive({
selector: '[appPreventDoubleClick]'
})
export class PreventDoubleClickDirective implements OnInit, OnDestroy {
@Input()
throttleTime = 500;
@Output()
throttledClick = new EventEmitter();
private clicks = new Subject();
private subscription: Subscription;
constructor() { }
ngOnInit() {
this.subscription = this.clicks.pipe(
throttleTime(this.throttleTime)
).subscribe(e => this.emitThrottledClick(e));
}
emitThrottledClick(e) {
this.throttledClick.emit(e);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
@HostListener('click', ['$event'])
clickEvent(event) {
event.preventDefault();
event.stopPropagation();
this.clicks.next(event);
}
}
throttleTime
はディレクティブにデフォルトの500があるためオプションです
<button appPreventDoubleClick (throttledClick)="log()" [throttleTime]="700">Throttled Click</button>
1msごとに要素をクリックしているボットがある場合、throttleTime
が起動するまでイベントが1回だけ発生することがわかります。