我在使用RxJ时遇到问题。
单击一次时,我需要在console.log中记录一条消息,而单击按钮两次时,我需要在日志中记录一条消息。问题是:
-没有任何反应-错误 (我应该看到第一条消息“单击”)
然后,如果我单击按钮,然后(一秒钟后)再次单击,则第二次单击我会看到两条消息-错误(每次操作我应该只看到一条消息)
< / li>我想要的是:
有什么帮助吗?
答案 0 :(得分:2)
一个非常简单的答案(不使用任何可观察变量)是使用setTimeout()
并检查每次点击是否设置了超时,如果这样,则表示这是给定时间范围内的第二次点击(两次点击),如果不是,则是第一次点击。如果超时到期,则您只需单击一下即可,就像这样:
// 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 :(得分: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被加载)。
这是给你的惊喜:
https://stackblitz.com/edit/angular-t6cvjj
请原谅我草率的类型声明!
这满足您的要求,如下:
由于在SO帖子中讨论的时间问题,如何解决这个问题是一个优先事项,因此我决定不解决它。而且,您不付钱给我;)但是,此答案应该为您提供了一个完全解决之道的好方法。
PS在上面的SO帖子上有一条评论表明event.detail可能不适用于IE11
答案 2 :(得分: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));
答案 3 :(得分:0)
您可以创建一个监听nologging
的指令,然后在该指令中执行有需要的操作。
dblclick
模板:
import { Directive, HostListener } from '@angular/core';
@Directive({
selector: '[appDoubleClick]'
})
export class DoubleClickDirective {
constructor() { }
@HostListener('dblclick') onDoubleClicked() {
console.log('onDoubleClicked ran!');
}
}
不确定在iOS中是否可以使用。但是请尝试一下。