14

I have the following implementation of a directive. How to removeEventListener in this case:

import { Directive, ElementRef, OnDestroy } from "@angular/core";

@Directive({
    selector: "[Enter]"
})
export class Enter implements OnDestroy{
    constructor(el: ElementRef) {
        let enter = function(event){
            if(event.keyCode === 13){
                el.nativeElement.click();
            }
        }
        document.addEventListener('keyup', enter , false);
    }

    ngOnDestroy(){
        document.removeEventListener('keyup', enter, false); //this line doesn't work because I can't access enter variable here!
    }
}

I know I can simply use a global variable here and can access it. But I don't want to store the state of instance in the global variable.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437

5 Answers5

28

I would leverage @HostListener decorator to do that:

@Directive({
  selector: "[Enter]"
})
export class Enter {
  @HostListener('document:keyup', ['$event'])
  enter(event) {
    if (event.keyCode !== 13) return;
    this.el.nativeElement.click();
  }
  constructor(private el: ElementRef) { }
} 

The handler will be automatically removed in ngOnDestroy.

For other solutions see:

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • While `@HostListener` works great, it also makes (my) Angular change detection run for the parent component on each key press. Using `Observable.fromEvent(window, 'keydown').subscribe(...)` does not. I guess one could create a decorator to run the decorated method outside of Angular's zone, but I've not tried. – Arjan Apr 26 '18 at 07:55
12

This should solve the problem:

import { Directive, ElementRef, OnDestroy } from "@angular/core";

@Directive({
    selector: "[Enter]"
})
export class Enter implements OnDestroy{
    private enter;
    constructor(el: ElementRef) {
        this.enter = function(event){
            if(event.keyCode === 13){
                el.nativeElement.click();
                console.log("enter triggered");
            }
        }
        document.addEventListener('keyup', this.enter , false);
        console.log("Added event listener");
    }

    ngOnDestroy(){
        document.removeEventListener('keyup', this.enter, false);
        console.log("Removed event listener"); 
    }
}

Hope this helps.

Cheers, SZ

ShellZero
  • 4,415
  • 12
  • 38
  • 56
  • As referred above by @jonrsharpe I cannot simply use 'any' there! –  Sep 28 '16 at 19:01
  • Your answer is what I have used in my codebase! Thanks a lot @ShellZero –  Sep 28 '16 at 21:17
5

Make it like this:

import { Directive, ElementRef, OnDestroy } from "@angular/core";

@Directive({
    selector: "[Enter]"
})
export class Enter implements OnDestroy{

    private enter: (event: KeyboardEvent) => void;

    constructor(el: ElementRef) {
        this.enter = (event) => {
            if(event.keyCode === 13){
                el.nativeElement.click();
            }
        }
        document.addEventListener('keyup',  this.enter , false);
    }

    ngOnDestroy(){
        document.removeEventListener('keyup', this.enter, false);
    }
}
VRPF
  • 3,118
  • 1
  • 14
  • 15
  • type function to the addEventListener is wrong! cannot pass this.enter like that! Maybe instead of created enter as a Function. It is better to create it as a enter: any; And then reference it as this.enter. That works. –  Sep 28 '16 at 18:44
  • @Marley you should type it as whatever it is. If you use `any` you might as well just use vanilla ES6. – jonrsharpe Sep 28 '16 at 18:46
  • But I'm getting the error, that you cannot pass a variable type Function to the EventListener Object –  Sep 28 '16 at 19:00
  • Thank you @VRPF It helps :) –  Sep 28 '16 at 20:04
1

As of today, the "Angular way" is to inject a Renderer2 dependency and work with that, to abstract away actual DOM manipulation when working on a platform different than browser (e.g. native or server-side rendering).

See this SO answer.

Community
  • 1
  • 1
superjos
  • 12,189
  • 6
  • 89
  • 134
0

Working DEMO: https://plnkr.co/edit/ZYnlruYQ2HwrQpHZqV9O?p=preview

NOTE: In DEMO, I use blur event instead of ngDestroy. (which serve the same purpose). If you type anything in textbox, it will listen to keyup event but as you come out from textbox input , blur event happens and no further keyup will get fired.

import { Directive, ElementRef, OnDestroy } from "@angular/core";

@Directive({
   selector: "[Enter]"
})
export class Enter implements OnDestroy{
    constructor(el: ElementRef) {
      var button=el.nativeElement;
      button.addEventListener('keyup',this.error)
    }

    error(event){
      console.log(event);
        //whatsoever
      if(event.keyCode === 13){
          el.nativeElement.click();
      }
    }

    ngOnDestroy(){
        button.removeEventListener('keyup',this.error); 
    }    
}
micronyks
  • 54,797
  • 15
  • 112
  • 146