Reputation: 6742
I need to distinguish between short (< 400ms) and long (> 400ms) clicks. I've implemented both events with RxJs, but the short click is emitting way more than it should if the user starts to click fast on the element.
app.component.html
<button #button (click)="onBuiltInClickEvent()">Click</button>
<p>Method was called: {{ calledTimes }}</p>
<p>Native click was called: {{ nativeClick }}</p>
app.component.ts
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit {
@ViewChild("button")
public button: ElementRef;
public calledTimes: number = 0;
public nativeClick: number = 0;
private mouseDown$: Observable<MouseEvent>;
private mouseUp$: Observable<MouseEvent>;
private click$: Observable<MouseEvent>;
public ngAfterViewInit(): void {
this.mouseDown$ = fromEvent(this.button.nativeElement, "mousedown");
this.mouseUp$ = fromEvent(this.button.nativeElement, "mouseup");
this.click$ = this.mouseDown$.pipe(
flatMap(_ => {
return this.mouseUp$.pipe(timeoutWith(400, EMPTY));
})
);
this.click$.subscribe(_ => this.onClick());
}
private onClick(): void {
this.calledTimes++;
}
public onBuiltInClickEvent(): void {
this.nativeClick++;
}
}
Working stackblitz.
Upvotes: 1
Views: 580
Reputation: 31125
flatMap
wouldn't be the suitable map operator in this scenario. You do not wish to flatten (or merge) the inner observable. In this scenario, only one inner subscription should be active at a time. So you could use switchMap
operator.
I also see that you aren't handling the error from the timeout or resubscribing to the observable if it times out. You could use RxJS retryWhen
operator for that. Try the following
this.click$ = this.mouseDown$.pipe(
switchMap(_ => this.mouseUp$.pipe(
timeout(400),
retryWhen(e => of('timeout error'))
))
);
I've modified your Stackblitz
Upvotes: 2