如何防止Angular双击?

Téw*_*éwa 8 rxjs angular-directive angular

我有一个组件click.

<my-box (click)="openModal()"></my-box>
Run Code Online (Sandbox Code Playgroud)

当我单击此元素时,openModal函数将运行.而且我想给出1000ms的节流时间以防止打开多个模态.

我的第一种方法是使用Subject(来自rxJs)

//html
<my-box (click)="someSubject$.next()"></my-box>
//ts
public someSubject$:Subject<any> = new Subject();
...etc subscribe
Run Code Online (Sandbox Code Playgroud)

但我觉得它有点冗长.

Next Approach正在使用a 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>
Run Code Online (Sandbox Code Playgroud)

但是,无论我尝试什么,总是openModal被执行.我找不到如何openModal在指令中停止执行.

我可以这样做

//ts
//In the openModal method.
openModal() {
    public isClickable = true

    setTimeout(() => {
        this.newsClickable = true;
    }, 1000);
    ...
}
Run Code Online (Sandbox Code Playgroud)

但对于可重用的代码,我认为使用指令是理想的.

我该怎么做?

Jon*_*arr 41

由于有人要求该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);
  }
}
Run Code Online (Sandbox Code Playgroud)

示例用法

throttleTime 是可选的,因为指令中有一个默认值 500

<button appPreventDoubleClick (throttledClick)="log()" [throttleTime]="700">Throttled Click</button>
Run Code Online (Sandbox Code Playgroud)

如果您有一个机器人每 1 毫秒点击一次您的元素,那么您会注意到该事件在throttleTime启动之前只会触发一次。

  • 这太棒了。您还可以使用throttleTime `config` `{leading: false, Trailing: true}` 扩展指令以仅允许双击。默认情况下它是“{leading: true, Trailing: false }”。 (3认同)

Sam*_*ann 17

您可以使用RxJs' 反跳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);
  }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

<button appDebounceClick (debounceClick)="log()" [debounceTime]="700">Debounced Click</button>
Run Code Online (Sandbox Code Playgroud)


Ste*_*erg 12

我提出了一种更简单的按钮方法:

    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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

用法示例:

    <button n-submit (click)="doSomething()"></button>
Run Code Online (Sandbox Code Playgroud)


rep*_*epo 7

就我而言throttleTime,最好的解决方法是防弹跳(立即触发并阻塞,直到经过一段时间)

  • 如何发布答案中给出的示例代码? (2认同)