如何防止在Angular中双击?

时间:2018-07-17 21:31:11

标签: angular rxjs angular-directive

我有一个带有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);
    ...
}

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

我该怎么做?

7 个答案:

答案 0 :(得分:12)

您可以使用RxJs的debouncedebounceTime运算符来防止双击。 Here还是有关如何创建自定义去抖动点击指令的帖子。

以防将来删除帖子,这是最终代码:

指令:

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>

答案 1 :(得分:4)

在我的情况下,throttleTime而不是去抖动是更好的解决方案(立即触发事件并在一段时间之前阻止)

答案 2 :(得分:3)

由于有人要求使用throttleTime指令,因此我将其添加到下面。我之所以选择这条路线,是因为debounceTime等待最后的点击,然后才触发实际的点击事件。 throttleTime将不允许点击器在该时间之前再次单击该按钮,而是立即触发click事件。

指令

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触发为止。

答案 3 :(得分:1)

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

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>

答案 4 :(得分:0)

还是想防止多次单击按钮?我正在使用以下解决方案:

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;
    }
}

我不知道它是否是最有效,最优雅的,但是它可以工作。

答案 5 :(得分:0)

我会使用自定义指令。

将其放在模板中的某个位置:

<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();
  }

}

答案 6 :(得分:0)

下面的代码对我有用,可以防止双击。

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>