Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Ember-backed autocomplete

In my journey in figuring out the Ember pattern, this is my attempt at trying to create an Ember-only autocomplete field. There were a few outcomes I wanted out of this, a part from being the Ember-way:

  • Work with any data source
  • Easily templatable results
  • Use only Ember constructs

All are welcome to use this, I'm just after feedback at this point.

Zalevent.Autocomplete = Ember.ContainerView.extend({
content: null,
value: null,
valuePath: '',
selected: null,
isDropdownVisible: false,
template: Ember.Handlebars.compile('{{view.content}}'),
classNames: 'autocomplete',
childViews: ['inputView', 'dropdownView'],
emptyView: null,
inputView: Ember.TextField.extend({
value: function(key, value) {
var parentView = this.get('parentView'),
if (arguments.length === 2) {
return value;
} else {
valuePath = parentView.get('valuePath').replace(/^content\.?/, '');
if (valuePath) { valuePath = '.' + valuePath; }
return parentView.get('value' + valuePath);
}.property('parentView.value', 'parentView.valuePath'),
keyUp: function(e) {
var parentView = this.get('parentView');
// Only trigger search when it's not a special key. Having this
// triggered when value changes gives us false positives as to
// the user's true intensions.
if (!parentView.constructor.KEY_EVENTS[e.keyCode]) {
parentView.trigger('search', this.get('value'));
dropdownView: Ember.CollectionView.extend({
classNames: 'dropdown',
tagName: 'ul',
contentBinding: 'parentView.content',
selectedBinding: 'parentView.selected',
templateBinding: 'parentView.template',
isVisibleBinding: 'parentView.isDropdownVisible',
emptyViewBinding: 'parentView.emptyView',
itemViewClass: Ember.View.extend({
tagName: 'li',
templateBinding: 'parentView.template',
classNameBindings: ['selected'],
selected: function() {
var content = this.get('content'),
value = this.get('parentView.selected');
return content === value;
click: function() {
var parentView = this.get('parentView.parentView'),
content = this.get('content');
if (parentView) {
parentView.trigger('select', content);
keyDown: function(e) {
var map = this.constructor.KEY_EVENTS,
method = map[e.keyCode];
if (method && Ember.typeOf(this[method]) === 'function') {
focusIn: function() {;
focusOut: function() {
setTimeout(Ember.$.proxy(this, 'hide'), 200);
select: function(value) {
this.set('value', value).hide();
search: function(term) {
var controller = this.get('controller');
if (term) {
controller.send('search', term, this);
confirm: function() {
var selected = this.get('selected');;
clear: function() {
value: null,
selected: null
next: function() {
return this._move(+1, this.get('content.firstObject'));
prev: function() {
return this._move(-1, this.get('content.lastObject'));
show: function() {
this.set('isDropdownVisible', true);
return this;
hide: function() {
this.set('isDropdownVisible', false);
return this;
_move: function(dir, def) {
var selected = this.get('selected'),
content = this.get('content'),
index = content.indexOf(selected);
if (index !== -1) {
selected = content.objectAt(index + dir);
} else {
selected = def;
this.set('selected', selected).show();
return selected;
contentDidChange: function() {;
Zalevent.Autocomplete.KEY_EVENTS = {
38: 'prev',
40: 'next',
27: 'clear',
13: 'confirm'
AutocompleteController = Ember.Controller.extend({
search: function(term, context) {
var results = [
Ember.Object.create({name: 'Bison'}),
Ember.Object.create({name: 'Vega'}),
context.set('content', results);
{{#view Autocomplete valueBinding="person" controllerBinding="controllers.autocomplete"}}

This comment has been minimized.

Copy link

@mszoernyi mszoernyi commented May 23, 2013

where do you initiate the AutoCompleteController to pass it to your view?


This comment has been minimized.

Copy link

@onamission onamission commented Jun 11, 2013

@evilmarty, Thanks for this widget, but I can't seem to get it to work. Since I am new to Ember, I might need a little more hand-holding. When I copied and pasted exactly as is, I got an error: "Uncaught ReferenceError: Zalevent is not defined ". So, I changed the name of the object to what I am using for my application object [App] and now no matter what I do I get an error like this: "Uncaught Error: assertion failed: Unable to find view at path 'Autocomplete' "

Any help would be appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment