单击并单击RxJs双击操作

问题描述 投票:0回答:4

我有一个RxJs的问题。

当我点击一次时,我需要在console.log中记录一条消息,当我在按钮上单击两次时,我需要记录不同的消息。问题是:

  • 在我开始时,如果我第一次点击 - 没有任何反应 - 错误(我应该看到第一条消息'一键')
  • 然后,如果我点击按钮然后(一秒钟后)我再次点击,第二次点击我可以看到两条消息 - 错误(我应该看到每个动作只有一条消息)
  • 当我单击按钮,等待一秒钟,再次单击,等待一秒钟再次单击,然后在最后一次单击时,我将在console.log中看到三条消息 - 错误(我应该看到每个操作只有一条消息)
  • 之后,如果我点击两次(双击),我会看到五条消息双击,一条一条点击 - 错误(我应该只看到一条消息'双击')

我想要的只是:

  • 如果我点击一次,我只需要看一条消息('一键')
  • 如果我点击两次(双击),仍然只需要看一条消息('双击')
  • 如果我点击两次以上 - 没有任何反应

任何帮助?

check hear for examples

javascript angular rxjs
4个回答
2
投票

一个非常简单的答案(不使用任何observable)是使用setTimeout()并检查每次点击是否已设置超时,如果是,你知道它是在给定时间窗口内的第二次点击(双击),如果没有,它是第一次点击。如果超时到期,您知道只需单击一次,如下所示:

Updated StackBlitz

// have a timer that will persist between function calls
private clickTimeout = null;
public test(event): void {
  // if timeout exists, we know it's a second click within the timeout duration
  // AKA double click
  if (this.clickTimeout) {
    // first, clear the timeout to stop it from completing
    clearTimeout(this.clickTimeout);
    // set to null to reset
    this.clickTimeout = null;
    // do whatever you want on double click
    console.log("double!");
  } else {
  // if timeout doesn't exist, we know it's first click
    this.clickTimeout = setTimeout(() => {
      // if timeout expires, we know second click was not handled within time window
      // so we can treat it as a single click
      // first, reset the timeout
      this.clickTimeout = null;
      // do whatever you want on single click
      console.log("one click");
    }, 400);
  }
}

编辑

我错过了忽略任何超过2次点击的部分。这并没有那么多工作,但是为了能够重用代码,我把它分解了一些,所以它看起来更像。无论如何,要忽略3次以上的点击,它将如下所示:

// count the clicks
private clicks = 0;
private clickTimeout = null;
public test(event): void {
  this.clicks++;
  if (this.clickTimeout) {
    // if is double click, set the timeout to handle double click
    if (this.clicks <= 2) {
      this.setClickTimeout(this.handleDoubleClick);
    } else {
    // otherwise, we are at 3+ clicks, use an empty callback to essentially do a "no op" when completed
      this.setClickTimeout(() => {});
    }
  } else {
    // if timeout doesn't exist, we know it's first click - treat as single click until further notice
    this.setClickTimeout(this.handleSingleClick);
  }
}
// sets the click timeout and takes a callback for what operations you want to complete when the
// click timeout completes
public setClickTimeout(cb) {
  // clear any existing timeout
  clearTimeout(this.clickTimeout);
  this.clickTimeout = setTimeout(() => {
    this.clickTimeout = null;
    this.clicks = 0;
    cb();
  }, 400);
}
public handleSingleClick() {
  console.log("one click");
}
public handleDoubleClick() {
  console.log("double!");
}

1
投票

@Siddharth Ajmeras的答案显示了如何通过事件来做到这一点。我不知道存在dblclick事件。你知道的越多。如果您仍然对如何使用rxjs感兴趣,这里有一个例子。

// How fast does the user has to click
// so that it counts as double click
const doubleClickDuration = 100;

// Create a stream out of the mouse click event.
const leftClick$ = fromEvent(window, 'click')
// We are only interested in left clicks, so we filter the result down
  .pipe(filter((event: any) => event.button === 0));

// We have two things to consider in order to detect single or
// or double clicks.

// 1. We debounce the event. The event will only be forwared 
// once enough time has passed to be sure we only have a single click
const debounce$ = leftClick$
  .pipe(debounceTime(doubleClickDuration));

// 2. We also want to abort once two clicks have come in.
const clickLimit$ = leftClick$
  .pipe(
    bufferCount(2),
  );


// Now we combine those two. The gate will emit once we have 
// either waited enough to be sure its a single click or
// two clicks have passed throug
const bufferGate$ = race(debounce$, clickLimit$)
  .pipe(
    // We are only interested in the first event. After that
    // we want to restart.
    first(),
    repeat(),
  );

// Now we can buffer the original click stream until our
// buffer gate triggers.
leftClick$
  .pipe(
    buffer(bufferGate$),
    // Here we map the buffered events into the length of the buffer
    // If the user clicked once, the buffer is 1. If he clicked twice it is 2
    map(clicks => clicks.length),
  ).subscribe(clicks => console.log('CLicks', clicks));

1
投票

这是你想要的或非常接近它:

import { Component, ViewChild, ElementRef } from '@angular/core';
import { fromEvent } from 'rxjs';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})

export class AppComponent  {

  @ViewChild('mybutton') button: ElementRef;

  ngAfterViewInit() {
    fromEvent(this.button.nativeElement, 'dblclick')
    .subscribe(e => console.log('double click'));

    fromEvent(this.button.nativeElement, 'click')
    .subscribe((e: MouseEvent) => {
      if (e.detail === 1) console.log('one click') // could use a filter inside a pipe instead of using an if statement
      // if (e.detail === 2) console.log('double click') // an alternative for handling double clicks
    });
  }
}

和HTML:

<button #mybutton>Test</button>

这个解决方案使用event.detail并且松散地基于这个有用的帖子 - Prevent click event from firing when dblclick event fires - 它不仅讨论了event.detail,还讨论了所涉及的时间问题。

您的代码的一个重要原因是您在多次调用的函数内订阅事件,这意味着每次单击该按钮时您都会创建另一个订阅。使用ngAfterViewInit(仅作为生命周期的一部分调用一次)可以防止出现此问题(并确保加载DOM)。

这是给你的stackblitz:

https://stackblitz.com/edit/angular-t6cvjj

原谅我的草率类型声明!

这满足您的要求如下:

  • 如果我点击一次,我只需要看一条消息('一键') - 通过
  • 如果我点击两次(双击),我仍然只需要看一条消息('双击') - 失败,你看到第一次点击'一键',第二次点击'双击'
  • 如果我点击两次以上 - 没有任何反应 - 通过

由于SO帖子中讨论的时间问题,你如何解决这个问题是一个偏好问题,因此我决定不解决它。而且,你不付钱给我;)然而,这个答案应该会帮助你完全解决问题。

PS上面有关SO帖子的评论表明event.detail可能无法在IE11上运行


0
投票

您可以创建一个侦听dblclick的指令,然后在指令中执行必要的操作。

import { Directive, HostListener } from '@angular/core';

@Directive({
  selector: '[appDoubleClick]'
})
export class DoubleClickDirective {
  constructor() { }

  @HostListener('dblclick') onDoubleClicked() {
    console.log('onDoubleClicked ran!');
  }

}

模板:

<button appDoubleClick (click)="test($event)">Test</button>

UPDATED STACKBLITZ

不确定这是否适用于iOS。但请试一试。

© www.soinside.com 2019 - 2024. All rights reserved.