Unsubscribing EventListener during ngOnDestroy

Here is my implementation of a directive in Angular. I am facing an issue with removing the event listener 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(){
        //How can I remove the event listener properly here?
    }
}

I understand that using a global variable to access 'enter' function would solve the issue, but I prefer not storing the state of instance in a global variable.

Answer №1

One approach would be to utilize the @HostListener decorator in this scenario:

@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) { }
} 

It's worth noting that the handler will be automatically removed in the ngOnDestroy lifecycle hook.

If you're interested in exploring alternative solutions, you can refer to:

  • How to listen for mousemove event on Document object in Angular

Answer №2

Here is a potential solution:

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

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

    ngOnDestroy(){
        document.removeEventListener('keyup', this.enterKeyHandler, false);
        console.log("Event listener removed"); 
    }
}

I hope this resolves your issue.

Best regards, SZ

Answer №3

Transform it in this way:

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

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

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

    constructor(el: ElementRef) {
        this.enterKeyHandler = (event) => {
            if(event.keyCode === 13){
                el.nativeElement.click();
            }
        }

        document.addEventListener('keyup',  this.enterKeyHandler , false);
    }

    ngOnDestroy(){
        document.removeEventListener('keyup', this.enterKeyHandler, false);
    }
}

Answer №4

Currently, the recommended approach in Angular is to utilize a Renderer2 dependency for handling DOM manipulation in order to abstract away the specifics when working on non-browser platforms (such as native apps or server-side rendering).

For more information, refer to this StackOverflow answer.

Answer №5

Check out the LIVE DEMO here: https://plnkr.co/edit/ZYnlruYQ2HwrQpHZqV9O?p=preview

IMPORTANT: This DEMO utilizes the blur event instead of ngDestroy, both serving a similar purpose. When typing in the textbox, it will detect the keyup event; however, when exiting the input field, the blur event is triggered, preventing any more keyup events.

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);
        //whatever needs to be done
      if(event.keyCode === 13){
          el.nativeElement.click();
      }
    }

    ngOnDestroy(){
        button.removeEventListener('keyup',this.error); 
    }    
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Calculating the sum of values in a JSON array using a specific parameter in Typescript

A flat JSON array contains repetitive identifier, categoryId, and category: data: [ { "identifier": "data", "categoryId": "1", "category": "Baked goods", "product": "Aunt Hattie's", "price": "375" } ...

There seems to be an issue with the functionality of Angular Material on iOS devices

I recently developed a website using Angular and Angular Material. While the site functions properly on Windows and Android across all browsers, I encountered an issue with iOS devices running Safari. Some elements on the page do not display correctly on i ...

Implementing a wrapped object check using a union type

Exploring the use of union types with basic primitives and custom objects, I created a contrived example inspired by the sample union type shown in the Typescript documentation under the Union Types section. In this example, I introduced a fictional type c ...

Attempting to execute the command "ionic serve" results in the display of various error messages

When attempting to run “ionic serve”, I encountered the following errors: [ERROR] ng has unexpectedly closed (exit code 127) https://i.sstatic.net/xgeb7.jpg Thank you in advance. ...

Why does TypeScript assign parameters in the opposite direction when assigning callbacks?

When a function is called with an argument, the argument type is matched to the parameter type. This means that the argument type must be the same as, or more specific than, the parameter type. For example: const foo = (bar: string | number) => { con ...

How can I resolve the infinite loop issue caused by Angular Auth guard when using routing?

My current struggle lies within the authentication guard logic and routing setup. In my app-routing.module.ts file, I have defined 3 routes: const routes: Routes = [ { path: '', loadChildren: () => import('./browse/browse.mod ...

Setting up only two input fields side by side in an Angular Form

I'm fairly new to Angular development and currently working on creating a registration form. I need the form to have two columns in a row, with fields like Firstname and Lastname in one row, followed by Phone, Email, Password, and Confirm Password in ...

What is the best way to make mouse and touch events trigger responses in Angular versions 9 and above?

I've been searching high and low for a library or tried-and-true method to handle common user events reactively, but unfortunately my quest has come up empty. After some digging, I stumbled upon what seemed like a solid solution: https://github.com/t ...

What is the best way for the frontend to access the information of the user currently logged in?

When users authenticate on my endpoint by providing their username and password, a JWT is returned if the credentials are correct. Now, I want to be able to show user-specific information after they log in. How can the frontend determine which user is cur ...

Two appearances of the record indicate that it has been updated

Whenever I update a record, it ends up appearing twice on the screen. This is how I am loading it: ngOnInit() { this.loadItems(); } loadItems(){ this.first_time_enter = true; console.log(this.first_time_enter); ...

Limiting the use of the Tab key within a modal dialog box

Currently, I have implemented a modal window that appears when a .btn link is clicked. However, I am facing an issue where users can still navigate through links and buttons in the background by pressing the Tab key. This becomes problematic especially fo ...

Error TS2304: Unable to locate identifier 'RTCErrorEvent'

I am currently working on a WebRTC application using Quasar, typescript and Vue. In my code snippet below, I am encountering an issue where I don't get any errors in WebStorm (as it uses the project's eslint config), and I can navigate to the def ...

The FlatList glides effortlessly in any direction

My FlatList allows me to drag and move it in all directions (up/down/right/left) even though it appears vertically due to styling. The scroll bar still shows horizontally, which I want to disable. How can I achieve this? This is the code snippet for using ...

Dropdown with grouped options in Angular PrimeNG - displaying data other than the default label/value pair

Hello there, I've encountered some difficulties with the dropdown menu, specifically when it comes to organizing by groups. Initially, I faced challenges understanding the specific format required for the array used in options to populate the dropdow ...

Challenges Encountered with Server Operations in Next.js v14 - Issue with Cookie Modification

I am currently working on a project using Next.js v14 and I have run into an issue with Server Actions, specifically when attempting to modify cookies. Despite meticulously following the documentation and ensuring that my functions are classified as Server ...

When trying to run a unit test for my AppModule, I encountered an error indicating that AngularFireModule was

While working on a project using Angular with Firebase integration, I encountered an issue during unit testing with Karma. It seems that Karma is throwing errors related to components connected to services that utilize Firestore functionality. Here is the ...

"Following successful POST login and session storage in MongoDB, the session is unable to be accessed due

When sending login data by POST with the credentials: 'include' option from client server 5500 to backend server 3000, I ensure that my session data is properly stored in MongoDB thanks to the use of 'connect-mongodb-session'. In the ba ...

TS2392 error: Implementing multiple constructors in Angular 2 is not permitted

Is there a more efficient way to avoid using multiple constructors in a component? Currently, I am encountering the following error: 'Multiple constructor implementations are not allowed'. questions: any[]; constructor( service: QuestionSignup ...

The Angular component exported from one module cannot be utilized in a different module

Why am I unable to use a custom component exported in my AppModule within another module that is imported into the AppModule? Isn't an exported component supposed to be visible globally? I am attempting to utilize the CalendarComponent with the selec ...

Executing POST calls to a Google Apps Script

Everything was running smoothly. I managed to set up an endpoint using Google Apps Script that allowed end users to send a message to me (or another contact) and receive a copy of that message as well. The code for the POST request looked something like ...