Keyboard Events
Angular Components Decorators SPA Typescript Visual Studio Code

How to use HostListeners in an Angular Application

Welcome to today’s post.

In today’s post I will be explaining what host listeners are and how to use them within an Angular application.

A host listener is a decorator that is declared within an Angular component, allowing us to associate a browser event to an event handler.

I will be showing how we can use two different browser DOM events within our application components in useful ways.

Moreover, we will look at browser DOM events that are triggered by both keyboard keypresses and mouse events are the most applicable events that can be processed by the listener. First, I will show how we process DOM mouse events, then later will show how to handle keyboard events.

Handling Mouse Events

The two events I will be reviewing are the window:unload and the window:keydown events.

To detect when the browser window closes, we can use the window:unload event. This can be setup using the host listener decorator as shown:

export class AppComponent {
    @HostListener('window:unload', [ '$event' ])
        unloadHandler(event) {
            // ...
        }

    @HostListener('window:beforeunload', [ '$event' ])
        beforeUnloadHandler(event) {
            // ...
        }
}

To show how the window unloading DOM event works in our component, I will embed console logging into the event handler.

To use the host listener decorator, we import from the angular library:

import { Component, HostListener } from '@angular/core'

Inside of our component we include the event handlers as shown:

@HostListener('window:unload', [ '$event' ])
    unloadHandler(event) {
        console.log("window has been closed.");
    }
  
@HostListener('window:beforeunload', [ '$event' ])
    beforeUnloadHandler(event) {
        console.log("starting close event for window..");
    }

When the application is run, and the form window as shown is closed from the cross-hairs:

In the terminal console, we can see the order of events with the before… event preceding the unload.. event handler:

Note: If we can try redirecting our form back to another form after the unload event has been triggered, this will not work as the application itself will close before any other events are run.

So, the following actions including logging and un-subscribing from observables can be included in your before unload handler and will be safely executed:

@HostListener('window:beforeunload', [ '$event' ])
    beforeUnloadHandler(event) {
        console.log("starting close event for window..");
        console.log("unsubscribing from subscriptions..");
        this.genre$.unsubscribe();
    }

However, the following will NOT be properly completed:

@HostListener('window:beforeunload', [ '$event' ])
    beforeUnloadHandler(event) {
        console.log("starting close event for window..");
        console.log("unsubscribing from subscriptions..");
        this.genre$.unsubscribe();
        this.router.navigate(["/dashboards/books-dashboard"]); 
    }

As we can see, the abrupt window close event is ideal for closing subscriptions and this is confirmed by the console log:

Even though we cannot redirect the user from the page in the closing event, we can confirm if the user wishes to close the application from the unload page event by setting the returnValue in the event parameter as shown:

@HostListener('window:beforeunload', [ '$event' ])
    beforeUnloadHandler(event) {
        console.log("confiring  close event for window..");        
        event.returnValue = false;
		…
    }

When the form cross hair icon is clicked, the user is shown the confirmation dialog as shown:

Of course, once the user selects the Leave option the web application terminates without any possibility of redirection.

Handling Keyboard Events

With the next event, window:keydown I will show how we can trap keyboard events and utilize them effectively within a component where we are using keyboard input instead of a mouse or touchpad.

I will use keypresses to interact with a child component within our component.

The composite component is shown below with a tiled single selection filter and a grid containing results of a query:

Our child component is a group selection control which is used to filter queries.

The partial HTML template for the parent control and selection control is shown below:

<mat-card-content>
    <div>
        <label class="boldedLabel" for="selectGenre1">
            Genre: {{selectedGenre}}
        </label>
        <br />
        <app-single-select-group #singleSelect
            [availableItems]="genres" 
            [selectedItem]="selectedGenre" 
            (selectionChange)="eventSelection($event)">
        </app-single-select-group>
    </div>

    <div *ngIf="hasLoaded$ | async; else loading">
		…
    </div>
		…
</mat-card-content>

The child view declaration is shown below:

@ViewChild("singleSelect", {static: true}) 
    singleSelectComponent: SingleSelectGroupComponent; 

To use the key down event to interact with the single selection component we will use key presses to interact in the following ways:

  1. Provide a way to navigate to the next selectable genre in our single select tiles by using the right-arrow key.
  2. Use the Enter key to select a genre and apply this to our query filter.
  3. Use the Backspace key to return to the previous menu screen.

We use the following event host listener and handler code to implement the above requirement:

@HostListener('window:keydown', ['$event']) handleKeyDown(
  event: KeyboardEvent) {
    console.log("keyboard key pressed = " + event.key);
    if (event.key == "Backspace")
    {
        this.eventBack(event);
    }
    if (event.key == "ArrowRight")
    {
        var idx = this.singleSelectComponent
            .availableItems
 		    .indexOf(this.selectedGenre);
        if (idx > -1) 
        {
            idx = (idx < this.singleSelectComponent.availableItems.length-1) 
                ? idx+1 : 0;
            this.selectedGenre = 
                this.singleSelectComponent.availableItems[idx];
        }
    }     
    if (event.key == "Enter")
    {
        this.singleSelectComponent.onSelect(this.selectedGenre);
    }
}

The above interactions are depicted below in the visual:

You can see how useful the host listener is for custom components and how we can use them to provide additional means of input within our components. We have seen how they can be used to provide useful interactions utilising multiple forms of user input including keyboard interactions. 

We also saw how useful it was to detect abrupt closures of forms and release component resources back to the memory heap. The same method could also be used to log a user out of an application outside of normal application logout controls.

That is all for today’s post.

I hope you found this post useful and informative.

Social media & sharing icons powered by UltimatelySocial