Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Mike Brunt
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Mike Brunt@cfwhisperer )

Native Key-Combination Event-Binding Support In Angular 2 Beta 17

By Ben Nadel on

Last week, I experimented with dynamically parsing and binding DOM (Document Object Model) events using the EVENT_MANAGER_PLUGINS system. While that post more of a fun exploration of the Angular 2 template syntax and plugin system, I didn't realize that Angular 2 Beta 17 actually ships with native key-combination support. So, in essence, just about everything that I implemented in my previous post is already available by default in the Browser platform.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

From what I can see, this appears to be an all-but-undocumented feature. It is touched upon briefly in the "Basics: User Input" documentation; but, there is very little explanation of either the breadth or the limitations of the key-combination support. So, digging through the actual source code, here's what I can find.

First, the limitations. The native KeyEventsPlugin plugin only support keydown and keyup events, not keypress. And, these key combinations can only be bound to a specific element (or host) - the plugin doesn't appear to support the global "document:" or "window:" event-scope. There is also no implicit support for browser-overrides. Meaning, if you need to cancel the default-behavior of the key-combination, you have to do it yourself (with $event.preventDefault()).

Now, the features. The syntax for the key binding is very similar to what I had in my previous post. You basically start with the event-type and then add a series of dot-delimited modifiers. For example:

  • keydown.a
  • keydown.b
  • keydown.c
  • keydown.dot
  • keydown.Spacebar
  • keydown.meta.Enter
  • keydown.alt.Enter
  • keydown.control.Enter
  • keydown.shift.Enter
  • keydown.meta.o
  • keydown.meta.s
  • keydown.meta.f
  • keydown.escape

The "special key" modifiers are:

  • alt
  • control
  • meta - The Command key on Mac and the Windows key on Windows.
  • shift

There are then two replacement keys that are there just keep the syntax from breaking:

  • Space - Or, you can use "Spacebar".
  • Dot - Since the modifiers are dot-delimited.

Beyond that, the values in your event-type are either character literals or special values taken from the "key" property of the event object (ex, "ArrowRight" and "PageDown").

To see this in action, I've create a simple demo in which you can focus an Input (remember this doesn't work on the global event scope) and try a number of the native key-combinations:

  • <!doctype html>
  • <html>
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • Native Key-Combination Event Binding In Angular 2 Beta 17
  • </title>
  •  
  • <link rel="stylesheet" type="text/css" href="./demo.css"></lin>
  • </head>
  • <body>
  •  
  • <h1>
  • Native Key-Combination Event Binding In Angular 2 Beta 17
  • </h1>
  •  
  • <my-app>
  • Loading...
  • </my-app>
  •  
  • <!-- Load demo scripts. -->
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/17/es6-shim.min.js"></script>
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/17/Rx.umd.min.js"></script>
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/17/angular2-polyfills.min.js"></script>
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/17/angular2-all.umd.js"></script>
  • <!-- AlmondJS - minimal implementation of RequireJS. -->
  • <script type="text/javascript" src="../../vendor/angularjs-2-beta/17/almond.js"></script>
  • <script type="text/javascript">
  •  
  • // Defer bootstrapping until all of the components have been declared.
  • requirejs(
  • [ /* Using require() for better readability. */ ],
  • function run() {
  •  
  • ng.platform.browser.bootstrap( require( "App" ) );
  •  
  • }
  • );
  •  
  •  
  • // --------------------------------------------------------------------------- //
  • // --------------------------------------------------------------------------- //
  •  
  •  
  • // I provide the root application component.
  • define(
  • "App",
  • function registerApp() {
  •  
  • // Configure the App component definition.
  • ng.core
  • .Component({
  • selector: "my-app",
  •  
  • // In the following template, we are binding to the key-event
  • // support that comes with Angular 2 out-of-the-box. This allows
  • // us to bind to specific key combinations, including modifiers
  • // and key-identifiers as per the specification:
  • // --
  • // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
  • // --
  • // NOTE: This only works with keydown and keyup - NOT keypress.
  • // --
  • // CAUTION: The built-in key event plugin does not support
  • // binding to the GLOBAL scope for some reason (ie, document: or
  • // window:). As such, it can only be bound to a specific element.
  • template:
  • `
  • <p>
  • Focus the Input and then type
  • <em>(binding not supported on global scope)</em>.
  • </p>
  •  
  • <input
  • (keydown.Enter)="handleKeyEvent( $event, 'Enter' )"
  • (keydown.alt.Enter)="handleKeyEvent( $event, 'ALT + Enter' )"
  • (keydown.control.Enter)="handleKeyEvent( $event, 'Control + Enter' )"
  • (keydown.meta.Enter)="handleKeyEvent( $event, 'Meta + Enter' )"
  • (keydown.shift.Enter)="handleKeyEvent( $event, 'Shift + Enter' )"
  • (keydown.Escape)="handleKeyEvent( $event, 'Escape' )"
  • (keydown.ArrowLeft)="handleKeyEvent( $event, 'Arrow Left' )"
  • (keydown.ArrowUp)="handleKeyEvent( $event, 'Arrow Up' )"
  • (keydown.ArrowRight)="handleKeyEvent( $event, 'Arrow Right' )"
  • (keydown.ArrowDown)="handleKeyEvent( $event, 'Arrow Down' )"
  • (keydown.Dot)="handleKeyEvent( $event, 'Dot' )"
  • (keydown.Space)="handleKeyEvent( $event, 'Space' )"
  • (keydown.shift)="handleKeyEvent( $event, 'Shift' )"
  • (keydown.meta.b)="handleKeyEvent( $event, 'Meta + b' )"
  • (keydown.meta.o)="handleKeyEvent( $event, 'Meta + o' )"
  • (keydown.meta.s)="handleKeyEvent( $event, 'Meta + s' )"
  • (keydown.meta.i)="handleKeyEvent( $event, 'Meta + i' )"
  • (keydown.meta.p)="handleKeyEvent( $event, 'Meta + p' )"
  • (keydown.meta.f)="handleKeyEvent( $event, 'Meta + f' )"
  • (keydown.h)="handleKeyEvent( $event, 'H' )"
  • (keydown.e)="handleKeyEvent( $event, 'E' )"
  • (keydown.l)="handleKeyEvent( $event, 'L' )"
  • (keydown.o)="handleKeyEvent( $event, 'O' )"
  • (keydown.1)="handleKeyEvent( $event, '1' )"
  • (keydown.2)="handleKeyEvent( $event, '2' )"
  • (keydown.3)="handleKeyEvent( $event, '3' )"
  • (keydown.4)="handleKeyEvent( $event, '4' )"
  • (keydown.5)="handleKeyEvent( $event, '5' )"
  • autofocus>
  •  
  • <ul>
  • <li>Enter</li>
  • <li>ALT + Enter</li>
  • <li>Control + Enter</li>
  • <li>Meta + Enter</li>
  • <li>Shift + Enter</li>
  • <li>Escape</li>
  • <li>Arrow Left</li>
  • <li>Arrow Up</li>
  • <li>Arrow Right</li>
  • <li>Arrow Down</li>
  • <li>Dot</li>
  • <li>Space</li>
  • <li>Shift</li>
  • <li>Meta + b</li>
  • <li>Meta + o</li>
  • <li>Meta + s</li>
  • <li>Meta + i</li>
  • <li>Meta + p</li>
  • <li>Meta + f</li>
  • <li>H</li>
  • <li>E</li>
  • <li>L</li>
  • <li>O</li>
  • <li>1</li>
  • <li>2</li>
  • <li>3</li>
  • <li>4</li>
  • <li>5</li>
  • </ul>
  • `
  • })
  • .Class({
  • constructor: AppController
  • })
  • ;
  •  
  • return( AppController );
  •  
  •  
  • // I control the App component.
  • function AppController() {
  •  
  • var vm = this;
  •  
  • // Expose the public events.
  • vm.handleKeyEvent = handleKeyEvent;
  •  
  •  
  • // ---
  • // PUBLIC METHODS.
  • // ---
  •  
  •  
  • // I handle the key event, logging the value.
  • function handleKeyEvent( event, value ) {
  •  
  • console.log( "Event (%s): %s", event.type, value );
  •  
  • // Since all of the key combinations represent some sort of
  • // native browser behavior, we're just going to cancel all
  • // the behaviors to keep the demo simple.
  • event.preventDefault();
  •  
  • }
  •  
  • }
  •  
  • }
  • );
  •  
  • </script>
  •  
  • </body>
  • </html>

As you can see, the only logic I'm providing is the event binding in my template and the logging in my controller; all of the actual key-event implementation is provided by Angular 2's Browser platform, out-of-the-box. And, when we run this demo and try some of the key-combinations, we get the following output:


 
 
 

 
 Native key-combination event-binding support in Angular 2 Beta 17. 
 
 
 

Awesome McAwesomeFace! The fact that Angular 2 supports key-combination event-binding out-of-the-box is like, totes powerful! I wish I had known about this earlier. Hopefully this helps shed some light for anyone else who may have completely missed this feature's existence in the Angular 2 guide.




Reader Comments

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.