Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Joel Hill
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Joel Hill

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

By
Published in Comments (5)

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.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel