如何防止在 Angular 中双击?
How to prevent double click in Angular?
我有一个组件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);
...
}
但对于可重用代码,我认为 using 指令是理想的。
我怎样才能做到?
您可以使用 RxJs 的 debounce or debounceTime operator to prevent double clicks. Here 也是关于如何创建自定义去抖动点击指令的 post。
以防将来post被取下,这里是最终代码:
指令:
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>
在我的例子中,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>
如果您有一个每 1 毫秒点击您的元素的机器人,那么您会注意到该事件只会触发一次,直到 throttleTime
启动。
我提出了一种更简单的按钮方法:
import {Directive, ElementRef, HostListener} from '@angular/core';
const DISABLE_TIME = 300;
@Directive({
selector: 'button[n-submit]'
})
export class DisableButtonOnSubmitDirective {
constructor(private elementRef: ElementRef) { }
@HostListener('click', ['$event'])
clickEvent() {
this.elementRef.nativeElement.setAttribute('disabled', 'true');
setTimeout(() => this.elementRef.nativeElement.removeAttribute('disabled'), DISABLE_TIME);
}
}
用法示例:
<button n-submit (click)="doSomething()"></button>
或者可能想要防止多次点击按钮?我正在使用以下解决方案:
import { Directive, HostListener } from '@angular/core';
@Directive({
selector: '[disableAfterClick]'
})
export class DisableButtonAfterClickDirective {
constructor() { }
@HostListener('click', ['$event'])
clickEvent(event) {
event.preventDefault();
event.stopPropagation();
event.currentTarget.disabled = true;
}
}
我不知道它是否是最有效和最优雅的,但它确实有效。
我会使用自定义指令。
将它放在模板中的某处:
<button appSingleClick (singleClick)="log()" [throttleMillis]="1000">click</button>
SingleClickDirective 指令
import {Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
import {fromEvent, Subscription} from 'rxjs';
import {throttleTime} from 'rxjs/operators';
@Directive({
selector: '[appSingleClick]'
})
export class SingleClickDirective implements OnInit, OnDestroy {
private subscription: Subscription;
@Input()
throttleMillis = 1500;
@Output()
singleClick = new EventEmitter();
constructor(private elementRef: ElementRef) {
}
ngOnInit(): void {
this.subscription = fromEvent(this.elementRef.nativeElement, 'click')
.pipe(throttleTime(this.throttleMillis))
.subscribe((v) => {
this.singleClick.emit(v);
});
}
ngOnDestroy(): void {
this.subscription?.unsubscribe();
this.singleClick.unsubscribe();
}
}
下面的代码可以防止双击。
onClick(event) {
const button = (event.srcElement.disabled === undefined) ? event.srcElement.parentElement : event.srcElement;
button.setAttribute('disabled', true);
setTimeout(function () {
button.removeAttribute('disabled');
}, 1000);
//Your code}
和HTML:
<button class="btn btn-save" (click)="onClick($event)">
Prevent Double click
</button>
我有一个组件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);
...
}
但对于可重用代码,我认为 using 指令是理想的。
我怎样才能做到?
您可以使用 RxJs 的 debounce or debounceTime operator to prevent double clicks. Here 也是关于如何创建自定义去抖动点击指令的 post。
以防将来post被取下,这里是最终代码:
指令:
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>
在我的例子中,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>
如果您有一个每 1 毫秒点击您的元素的机器人,那么您会注意到该事件只会触发一次,直到 throttleTime
启动。
我提出了一种更简单的按钮方法:
import {Directive, ElementRef, HostListener} from '@angular/core';
const DISABLE_TIME = 300;
@Directive({
selector: 'button[n-submit]'
})
export class DisableButtonOnSubmitDirective {
constructor(private elementRef: ElementRef) { }
@HostListener('click', ['$event'])
clickEvent() {
this.elementRef.nativeElement.setAttribute('disabled', 'true');
setTimeout(() => this.elementRef.nativeElement.removeAttribute('disabled'), DISABLE_TIME);
}
}
用法示例:
<button n-submit (click)="doSomething()"></button>
或者可能想要防止多次点击按钮?我正在使用以下解决方案:
import { Directive, HostListener } from '@angular/core';
@Directive({
selector: '[disableAfterClick]'
})
export class DisableButtonAfterClickDirective {
constructor() { }
@HostListener('click', ['$event'])
clickEvent(event) {
event.preventDefault();
event.stopPropagation();
event.currentTarget.disabled = true;
}
}
我不知道它是否是最有效和最优雅的,但它确实有效。
我会使用自定义指令。
将它放在模板中的某处:
<button appSingleClick (singleClick)="log()" [throttleMillis]="1000">click</button>
SingleClickDirective 指令
import {Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
import {fromEvent, Subscription} from 'rxjs';
import {throttleTime} from 'rxjs/operators';
@Directive({
selector: '[appSingleClick]'
})
export class SingleClickDirective implements OnInit, OnDestroy {
private subscription: Subscription;
@Input()
throttleMillis = 1500;
@Output()
singleClick = new EventEmitter();
constructor(private elementRef: ElementRef) {
}
ngOnInit(): void {
this.subscription = fromEvent(this.elementRef.nativeElement, 'click')
.pipe(throttleTime(this.throttleMillis))
.subscribe((v) => {
this.singleClick.emit(v);
});
}
ngOnDestroy(): void {
this.subscription?.unsubscribe();
this.singleClick.unsubscribe();
}
}
下面的代码可以防止双击。
onClick(event) {
const button = (event.srcElement.disabled === undefined) ? event.srcElement.parentElement : event.srcElement;
button.setAttribute('disabled', true);
setTimeout(function () {
button.removeAttribute('disabled');
}, 1000);
//Your code}
和HTML:
<button class="btn btn-save" (click)="onClick($event)">
Prevent Double click
</button>