Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

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