יובל צרפתי
יובל צרפתי

Reputation: 79

Angular - Add debounce to an event

I am using mouseenter and mouseleave events to handle sidenav opening and closing in my app. I would like to add a bit of debounce to these events, because right now if you hover the container with these events fast, these events are being called a lot of times and I'm getting weird behavior with my sidenav. How can I add debounce to these events ? Is it event possible ?

Upvotes: 3

Views: 11244

Answers (3)

michal.jakubeczy
michal.jakubeczy

Reputation: 9469

You can create a directive which you can add to your input then. In this example I debounce click event, but you can easily accomodate it to any other event.

import { Directive, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { Subject, Subscription } from 'rxjs';
import { debounceTime } from 'rxjs/operators';

@Directive({
    selector: '[debounceClick]'
})

export class DebounceClickDirective implements OnInit, OnDestroy {

    @Input()
    public debounceClickTime = 500;

    @Output()
    public debounceClick = new EventEmitter();

    private clicks = new Subject();
    private subscription: Subscription;

    constructor() { }

    public ngOnInit() {
        this.subscription = this.clicks.pipe(debounceTime(this.debounceClickTime)).subscribe((e: Event) =>
            this.debounceClick.emit(e));
    }

    public ngOnDestroy() {
        this.subscription.unsubscribe();
    }

    @HostListener('click', ['$event'])
    public clickEvent(event: Event) {
        event.preventDefault();
        event.stopPropagation();

        this.clicks.next(event);
    }

}

And use it in your HTML in a following way:

<input type="text" (debounceClick)="yourMethod()" debounceClickTime="1000" />

debounceClickTime is optional and if you do not use it, it debounces by default in 500ms.

Upvotes: 1

hanan
hanan

Reputation: 1880

RXJS has a function FromEvent which convert event to Stream u have to create two streams ,one on enter and other on leave

I think with debounceTime u should use distinctUntilChanged as well. otherwise it will emit true true, or false false , but u need true false or false true sequence

 @ViewChild('sizeMenu')
  set SizeMenu(sizeMenu: ElementRef) {
    let mouseEnter$ = fromEvent(sizeMenu.nativeElement, 'mouseenter').pipe(
     mapTo(true));

     let mouseLeave$ = fromEvent(sizeMenu.nativeElement, 'mouseleave').pipe(
     mapTo(false));


     this.mouseEvents$ = mouseLeave$.pipe(merge(mouseEnter$), debounceTime(this.DebounceTime), distinctUntilChanged());
  };

this is example on stackblitz

Upvotes: 1

G&#233;r&#244;me Grignon
G&#233;r&#244;me Grignon

Reputation: 4228

Add a template reference variable on your button :

<button #button>Click me</button>

Reference it inside your component using @ViewChild

@ViewChild('button') button: ElementRef;

Use fromEvent from rxjs to listen to the click event and use the debounceTime operator :

ngAfterViewInit() {
    fromEvent(this.button.nativeElement, 'click').pipe(
      debounceTime(2000) // 2 seconds
    ).subscribe(() => {
      // do whatever
    });
  }

Upvotes: 6

Related Questions