AngularJS Live Example

by JakobJingleheimer

HTML

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.js"></script>
<div ng-controller="listController">
    <form name="myForm" ng-submit="submit()">
        <ul class="list-inline">
            <li show-list="foos" input-id="foo" data-ng-model="data.foo" placeholder="foo…"></li>
            <li show-list="bars" input-id="bar" data-ng-model="data.bar" placeholder="bar…"></li>
            <li show-list="bazes" input-id="baz" data-ng-model="data.baz" placeholder="baz…"></li>
        </ul>
        <button type="submit">Send</button>
    </form>
</div>

<script type="text/ng-template" id="directiveTmpl">
<input
	autocomplete="off"
	id="{{ inputId }}"
	placeholder="{{ placeholder }}"
	type="text"
	ng-model="query" /><!--
	--><button
		class="btn-metal suggest-toggle"
		type="button"
		ng-click="showSuggestions = !showSuggestions"><!--
		--><i class="link icon-chevron-down"></i><!--
	--></button>
<ul class="suggestions" data-ng-show="showSuggestions">
	<li
		class="suggestion"
		index="{{ $index }}"
		value="{{ suggestion }}"
		ng-class="{ 'active': suggestion === query || $index === selectedSuggestionIndex }"
		ng-click="query = suggestion; selectedSuggestionIndex = $index"
		ng-repeat="suggestion in suggestions | filter:query">{{ suggestion }}</li>
</ul>
</script>

CSS

.list-inline {
    width: 100%;
}

.list-inline li {
    display: inline-block;
}

.suggestions {
	cursor: pointer;
	display: block;
	max-height: 10em;
	overflow: scroll;
	padding: 0.25em 0;
	position: absolute;
	width: 100%;
}

.suggestion {
    padding: 0.25em 0.5em;
}
.suggestion:hover {
    color: red;
}
.suggestion.active {
    background: red;
}

JavaScript

angular.module('ShowList', [])
.directive('showList',function ShowListDirective() {
	return {
		priority: 10000,
		templateUrl: 'directiveTmpl',
		restrict: 'A',
		scope: {
			suggestions: '=showList',
			inputId:     '@',
			query:       '=ngModel'
		},
		link: function postLink(scope, element, attrs) {
			scope.showSuggestions = false;
			element.addClass('show-list');

			var controlKeys = {
					down:  40,
					enter: 13,
					esc:   27,
					up:    38
				},
				formElm  = element.closest('form');

			if ( 'placeholder' in attrs ) {
				scope.placeholder = attrs.placeholder;
			}

			function selectUpDown(direction) {
				scope.selectedSuggestionIndex = angular.isDefined( scope.selectedSuggestionIndex ) ? scope.selectedSuggestionIndex : -1;
				var adjustBy = direction === 'up' ? -1 : 1,
					newIndex = scope.selectedSuggestionIndex + adjustBy;
				scope.$apply(function selectUpDownApply() {
					scope.selectedSuggestionIndex = newIndex > 0 ? newIndex : 0;
					scope.showSuggestions = true;
				});
			}

			formElm.on('submit', function(event) {
				if ( scope.showSuggestions ) { // <- this value is WRONG
					event.stopImmediatePropagation();
				}
			});

			element.on('keyup', function showListKeyup(event) {
				var key = event.keyCode || event.which;

				switch( key ) {
					case controlKeys.down:
						selectUpDown('down');
						break;
					case controlKeys.enter:
						if ( scope.showSuggestions ) {
							scope.selectedSuggestion = scope.selectedSuggestion || element.find('.active').val();
							if ( scope.selectedSuggestion ) {
								event.stopPropagation();
								scope.$apply(function acceptSuggestion() {
									scope.query = scope.selectedSuggestion;
									scope.showSuggestions = false;
								});
							}
						}
						break;
					case controlKeys.esc:
						event.preventDefault(); // so modals don't inadvertently close
						scope.$apply(function hideList(){
							scope.showSuggestions = false;
						});
						break;
					case...