Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Create a Keybinding Service in Angular
Josh Britz
Josh Britz

Posted on • Edited on • Originally published atjoshbritz.co

     

Create a Keybinding Service in Angular

A common frustration I come across when working with Angular (or any other framework for that matter), is running functions based on key bindings. The typical method of doing this is unreadable and messy. For example, if you wanted to run a function when a user hitCTRL+ALT+D, your code would look something like this.

window.addEventListener('keydown',(event:KeyboardEvent)=>{// Fall back to event.which if event.keyCode is nullconstkeycode=event.keyCode||event.which;if(keycode===68&&event.ctrlKey&&event.altKey){// Do stuff here}});

True, it could be worse. Never the less, doing this can become very messy
very quickly. It is also difficult to quickly find and fix problems because you have to know the exact number of each key code to know what key command is being listened for. In a recent project, I was working on, I decided to remedy the issue by abstracting the process into an Angular service and now I'm going to show you how to do it.

Creating a project

Because we are making an Angular service, you will need to have an Angular project to work in. You can use an existing one, or you can create a new blank project to play around with.

$ng new KeyBindServiceApp

You should now have a fresh project to work in.

Setting things up

First of all, we need to create a service that will do all our work.

$ng g service path/to/services/key-bind

Your newly created service should look something like this.

import{Injectable}from'@angular/core';@Injectable({providedIn:'root'})exportclassKeyBindService{constructor(){}}

In the same folder as your service, create two new files: one calledmodifiers.ts and one calledkeycodes.ts, and copy the following code into them. You can move these later, but for now, it is easier to keep them in the same folder as your service.

/*** Checks if an event has any of the provided* keycodes* @param {KeyboardEvent} event* @param {Array<number>} codes*/exportfunctionhasKeycode(event:KeyboardEvent,codes:number[]):boolean{returncodes.some(value=>(event.keyCode||event.which)===value);}exportconstKEYS={A:65,ALT:18,APOSTROPHE:192,AT_SIGN:64,B:66,BACKSLASH:220,BACKSPACE:8,C:67,CAPS_LOCK:20,CLOSE_SQUARE_BRACKET:221,COMMA:188,CONTEXT_MENU:93,CONTROL:17,D:68,DASH:189,DELETE:46,DOWN_ARROW:40,E:69,EIGHT:56,END:35,ENTER:13,EQUALS:187,ESCAPE:27,F:70,F1:112,F10:121,F11:122,F12:123,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,FF_EQUALS:61,FF_MINUS:173,FF_MUTE:181,FF_SEMICOLON:59,FF_VOLUME_DOWN:182,FF_VOLUME_UP:183,FIRST_MEDIA:166,FIVE:53,FOUR:52,G:71,H:72,HOME:36,I:73,INSERT:45,J:74,K:75,L:76,LAST_MEDIA:183,LEFT_ARROW:37,M:77,MAC_ENTER:3,MAC_META:224,MAC_WK_CMD_LEFT:91,MAC_WK_CMD_RIGHT:93,META:91,MUTE:173,N:78,NINE:57,NUM_CENTER:12,NUM_LOCK:144,NUMPAD_DIVIDE:111,NUMPAD_EIGHT:104,NUMPAD_FIVE:101,NUMPAD_FOUR:100,NUMPAD_MINUS:109,NUMPAD_MULTIPLY:106,NUMPAD_NINE:105,NUMPAD_ONE:97,NUMPAD_PERIOD:110,NUMPAD_PLUS:107,NUMPAD_SEVEN:103,NUMPAD_SIX:102,NUMPAD_THREE:99,NUMPAD_TWO:98,NUMPAD_ZERO:96,O:79,ONE:49,OPEN_SQUARE_BRACKET:219,P:80,PAGE_DOWN:34,PAGE_UP:33,PAUSE:19,PLUS_SIGN:43,PRINT_SCREEN:44,Q:81,QUESTION_MARK:63,R:82,RIGHT_ARROW:39,S:83,SCROLL_LOCK:145,SEMICOLON:186,SEVEN:55,SHIFT:16,SINGLE_QUOTE:222,SIX:54,SLASH:191,SPACE:32,T:84,TAB:9,THREE:51,TILDE:192,TWO:50,U:85,UP_ARROW:38,V:86,VOLUME_DOWN:174,VOLUME_UP:175,W:87,X:88,Y:89,Z:90,ZERO:48};exporttypeKeyNames=keyoftypeofKEYS;
exporttypeModifierKey='altKey'|'shiftKey'|'ctrlKey'|'metaKey';exportconstMODIFIERS=['altKey','shiftKey','ctrlKey','metaKey'];/** * Checks whether a modifier key is pressed. * @param event Event to be checked. */exportfunctionhasModifierKey(event:KeyboardEvent,...modifiers:ModifierKey[]):boolean{if(modifiers.length){returnmodifiers.some(modifier=>event[modifier]);}returnevent.altKey||event.shiftKey||event.ctrlKey||event.metaKey;}

These files contain helpers we will use to make our service work properly. For example,keycodes.ts has a list of all the key codes, mapped to readable names, andmodifiers has a helper function to check if one or more modifier keys are present in and event.

Theoretically, they would already make your life easier. You could use them as below.

window.addEventListener('keydown',(event:KeyboardEvent)=>{const{D}=KEYS;if(hasKeycode(event,D)&&hasModifierKey(event,'ctrlKey','altKey')){// Do action}});

This code is much cleaner, but we can do better than that. What we want is a declarative way of listening to key events.

In the key bind service, you will create a new method calledmatch(). We will have parameters so that you can specify the keys and modifiers you want to listen for, some extra options, and we will return an Observable that fires when the binding is matched.

publicmatch(matchKey:KeyNames,matchModifiers:ModifierKey[]=[],options?:MatchConfig):Observable<KeyboardEvent>{const{listenOn}=newMatchConfig(options);returnnewObservable((observer)=>{constlistener$=fromEvent(listenOn,'keydown');});}

And a class for your extra config

exportclassMatchConfig{publiclistenOn:EventTarget=window;constructor(init:Partial<MatchConfig>){Object.assign(this,init);}}

Now that we have out plumbing in place, we will need to subscribe to our keydown listener and check if the current event's keys match the binding specified. To do that, we will use the methods defined inkeycodes.ts andmodifiers.ts.

publicmatch(matchKey:KeyNames,matchModifiers:ModifierKey[]=[],options?:MatchConfig):Observable<KeyboardEvent>{const{listenOn}=newMatchConfig(options);returnnewObservable((observer)=>{constlistener$=fromEvent(listenOn,'keydown');listener$.subscribe((event:KeyboardEvent)=>{if(hasKeycode(event,KEYS[matchKey])&&(!matchModifiers.length||hasModifierKey(event,...matchModifiers))){observer.next(event);}});});}

*note we added(!matchModifiers.length || hasModifierKey(event, ...matchModifiers) to the if statement so that it can match a binding that has no modifiers.

The service is now ready to be used. You can provide it to a component through the constructor.

constructor(privatekeybind:KeyBindService){}

And to use it

constbinding$=this.keybind.match(KEYS.RIGHT_ARROW,['ctrlKey']).subscribe(()=>{alert('binding pressed');});// to stop listening to the binding call .unsubscribe on itbinding$.unsubscribe()

If you want to see this project in action, you can find it here.

GitHub logo joshuapbritz / KeybindingServiceInAngular

Example project for creating a keybinding service in Angular

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

UX Engineer and software tinkerer from South Africa
  • Location
    Johannesburg, South Africa
  • Work
    UX Engineer at OnePointFour Consulting
  • Joined

Trending onDEV CommunityHot

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp