I have a component with click
.
<my-box (click)="openModal()"></my-box>
When I click this element, openModal
function will run.
And I'd like to give 1000ms throttle time in order to prevent opening multiple modals.
My first approach was using Subject
(from rxJs)
//html
<my-box (click)="someSubject$.next()"></my-box>
//ts
public someSubject$:Subject<any> = new Subject();
...etc subscribe
But I feel it's a bit verbose.
Next Approach was using a directive
.
I modified a bit of code that I found by googling.
//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>
However, whatever I try, always openModal
was executed.
I couldn't find how to stop executing openModal
in the directive.
I can just make like
//ts
//In the openModal method.
openModal() {
public isClickable = true
setTimeout(() => {
this.newsClickable = true;
}, 1000);
...
}
But for the reusable code, I think using directive is ideal.
How can I make it?
You can use RxJs' debounce or debounceTime operator to prevent double clicks. Here is also a post on how to create a custom debounce click directive.
In case the post is taken down in the future, here is the final code:
import {
Directive,
EventEmitter,
HostListener,
Input,
OnDestroy,
OnInit,
Output
} from '@angular/core';
import { Subject, Subscription } from 'rxjs';
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>