Sometimes, you need to eavesdrop.
You have to "listen" to what your users are doing on the UI.
Why? Because you might need to take action based on a specific key that the user pressed.
In this guide, I'll show you how to write code that spies on your users. In a good way.
@HostListener
You're gonna do it with @HostListener.
But what the heck is that?
It's a decorator. That means it don't do nothin' by itself.
So you need to add it to a method. Your own method.
But before I demo that, let me explain the options associated with @HostListener.
Usually, you'll see @HostListener declared like this:
@HostListener('document:keyup', ['$event'])
The first option is the event to listen for. In this case, it's listening for the keyup event on the document object.
So keep this in mind: the first option is structured in terms of the Document Object Model (DOM) and not typical Angular coding.
So you won't use @ViewChild or ElementRef here. Instead, you'll use verbiage and syntax similar to what you'd use if you were handling this with old-school JavaScript on an HTML page.
Except for that colon bit. Remember: use a colon and not a period in that first option.
And even though the code above is listening for keyup on the document object, that's not all you can do with @HostListener.
For example: you can listen for click as well. That's useful when you need to know if a user clicked on a specific element on the UI.
The second option in @HostListener is an array of arguments that get passed to your custom method once the event occurs.
In the example above, the listener passes the well-known $event as the only parameter in the method.
So the decorator above is simply saying: "When someone finishes pressing a key, execute the method below and pass the most recent DOM event into it."
But what's "the method below"? I'll cover that in the next section.
Method to Your Madness
Here's how I use the @HostListener decorator above:
@HostListener('document:keyup', ['$event'])
handleKeyboardEvent(event: KeyboardEvent) {
if (event.key == 'Tab' && this.lastField == 'subject') {
var el: any = document.querySelectorAll('.ql-editor')
if (el && el.length == 1) {
el[0].focus();
this.lastField = 'body';
}
}
}
The decorator sits right on top of a method declaration so there's no cofusion about which method it's decorating. It's decorating the handleKeyboardEvent() method.
Unsurprisingly, that method accepts a single parameter of type KeyboardEvent. That's because the method gets triggered on the keyup DOM event.
And remember: the second option in @HostListener is an array containing a single element $event. That $event object maps to the only parameter in handleKeyboardEvent().
Now for this particular requirement, I need the application to tab to a specific field when the user tabs away from the "Subject" field on a form. I can explain why, but it would take a few paragraphs and may bore you.
So just accept the fact that I need to tab to a specific form field once the user tabs away from the "Subject" field.
That's what the code in handleKeyboardEvent() does. It checks to see if the last character pressed was the tab key. Then it also checks to see if the user's cursor is currently sitting in the "Subject" field. You can see all of that in the first if statement above.
If that check evaluates to true, then the code puts the first element discovered by querySelectAll() method in focus.
Wrapping It Up
That's it. That's all you need to do to listen for key presses on your UI.
Also worth noting: the solution above is non-destructive. In other words, the tab key will function normally if the user isn't on the "Subject" field.
You can use @HostListener that way as well.
Just make sure you have fun!
Photo by jonas mohamadi from Pexels