Post

Enhancing Angular Navigation with GlobalKeydown: A Guide to Keyboard-Driven Routes

Introduction to GlobalKeydownService

Angular’s flexibility allows us to extend our application’s functionality with custom services. In this case, we’ll create GlobalKeydownService. This service listens to global keydown events and filters out those triggered within input elements - ensuring that typing in a text field does not inadvertently trigger navigation.

The Service Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Injectable } from '@angular/core';
import { fromEvent, Observable } from 'rxjs';
import { filter, map } from 'rxjs/operators';

@Injectable({
    providedIn: 'root',
})
export class GlobalKeydownService {
    keydownEvents$: Observable<string>;

    constructor() {
        this.keydownEvents$ = fromEvent<KeyboardEvent>(window, 'keydown').pipe(
            filter((event) => !(event.target instanceof HTMLInputElement)),
            map((event) => event.key)
        );
    }
}

This service uses RxJS’s fromEvent to listen for keydown events on the window object, and then processes these events to output only the relevant key values.

Integrating GlobalKeydownService with NavbarComponent

With GlobalKeydownService in place, we can now modify NavbarComponent to listen to keydown events and navigate accordingly.

Modifying the Navbar Component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { Component, inject, OnInit } from '@angular/core';
import { Route, RouterModule } from '@angular/router';
import { NavigationService } from '../../shared/services/navigation.service';
import { GlobalKeydownService } from '../../shared/services/global-keydown.service';
import { Subject, takeUntil } from 'rxjs';

// ... Component Code ...

export class NavbarComponent implements OnInit {
    // ... Existing properties ...
    #keydown = inject(GlobalKeydownService);
    #destroy$ = new Subject();

    ngOnInit(): void {
        // ... Existing initialization code ...

        this.#keydown.keydownEvents$
            .pipe(takeUntil(this.#destroy$))
            .subscribe((key) => {
                this.navigateByKey(key);
            });
    }

    private navigateByKey(key: string): void {
        const index = parseInt(key, 10) - 1;
        if (index >= 0 && index < this.routes.length) {
            this.#navigation.navigateTo(this.routes[index].path as string);
        }
    }

    // ... Remaining component code ...
    ngOnDestroy(): void {
        this.#destroy$.next(true);
        this.#destroy$.complete();
    }

}

In this updated component, we are subscribing to the keydownEvents$ observable from GlobalKeydownService. When a key is pressed, navigateByKey method is called, which checks if the pressed key corresponds to a valid route index and, if so, triggers navigation to that route.

Angular Navigation

Conclusion

By integrating GlobalKeydownService with our navbar component, we’ve added a layer of keyboard-driven navigation to our Angular application. This approach not only enhances user experience by providing a quicker navigation method but also contributes to the accessibility of the application.

This post is licensed under CC BY 4.0 by the author.