J'ai un composant avec click
.
<my-box (click)="openModal()"></my-box>
Lorsque je clique sur cet élément, la fonction openModal
s'exécute. Et je voudrais donner un temps d'accélération de 1000 ms afin d'empêcher l'ouverture de plusieurs modaux.
Ma première approche consistait à utiliser Subject
(à partir de rxJs)
//html
<my-box (click)="someSubject$.next()"></my-box>
//ts
public someSubject$:Subject<any> = new Subject();
...etc subscribe
Mais je pense que c'est un peu bavard.
L'approche suivante utilisait un directive
. J'ai modifié un peu de code que j'ai trouvé en recherchant sur Google.
//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>
Cependant, quoi que j'essaye, toujours openModal
était exécuté. Je n'ai pas trouvé comment arrêter d'exécuter openModal
dans la directive.
Je peux juste faire comme
//ts
//In the openModal method.
openModal() {
public isClickable = true
setTimeout(() => {
this.newsClickable = true;
}, 1000);
...
}
Mais pour le code réutilisable, je pense que l'utilisation de la directive est idéale.
Comment puis-je le faire?
Vous pouvez utiliser l'opérateur debounce ou debounceTime de RxJs pour éviter les doubles-clics. Ici est également un article sur la façon de créer une directive de clic anti-rebond personnalisée.
Dans le cas où le poste serait retiré à l'avenir, voici le code final:
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>
Dans mon cas, throttleTime
au lieu de anti-rebond était une meilleure solution (déclencher immédiatement l'événement et bloquer jusqu'à ce qu'un certain temps se soit écoulé)
Puisque certaines personnes ont demandé la directive throttleTime
, je vais l'ajouter ci-dessous. J'ai choisi de suivre cette route car le debounceTime
attend le dernier clic avant de déclencher l'événement de clic réel. throttleTime
ne permettra pas au clicker de cliquer à nouveau sur le bouton jusqu'à ce que ce temps soit atteint et déclenche à la place l'événement click immédiatement.
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
est facultatif car il y a une valeur par défaut de 500 dans la directive
<button appPreventDoubleClick (throttledClick)="log()" [throttleTime]="700">Throttled Click</button>
Si vous avez un bot qui clique sur votre élément toutes les 1 ms, vous remarquerez que l'événement ne se déclenche qu'une seule fois jusqu'à ce que throttleTime
soit en place.