Ember is a framework for ambitious web developers, even though building application using Ember is faster, there were some bottlenecks like creating a select list using HTML and getting the data from the HTML element and submitting to the API etc.
Ember comes with a power-train called components and this will solve our problem, we should architect components carefully so that can be reused across. The solution is through mix of things which i experienced and there might be better way of solving the same problem.
Assumptions
Latest version of Ember is installed and you have basic experience of building a Application with knowledge of using HTML, Javascript, NodeJS, Ember Rest Adapter, Ember Model, Ember View and Ember Controller.
Step 1
Run the following command under Ember project folder
Step 2
Step 3
Edit your parent view file and copy the following code, in my case it is templates/todo.hbs
Step 5
Start your Ember server with the following command
Ember comes with a power-train called components and this will solve our problem, we should architect components carefully so that can be reused across. The solution is through mix of things which i experienced and there might be better way of solving the same problem.
Assumptions
Latest version of Ember is installed and you have basic experience of building a Application with knowledge of using HTML, Javascript, NodeJS, Ember Rest Adapter, Ember Model, Ember View and Ember Controller.
Step 1
Run the following command under Ember project folder
ember g component input-autocomplete
The above command will create a controller and view for the component. The controller will be created under component folder with name input-autocomplete.js and template with name input-autocomplete under template/component/input-autocomplete.hbs.
The following code should be copied into the component template.
{{input
name=this.name
value=this.value
placeholder=this.placeholder
key-up=(action "search")
focusIn=(action "showdropdown")
focusOut=(action "hidedropdown")
class="form-control"
}}
<div id="myInputautocomplete-list" class="autocomplete-items">
{{#each this.filteredResult as |data|}}
<div {{action 'choose' data.title}} {{action "choose" data.title on="mouseDown"}} >
{{data.title}}
<input type="hidden" value="{{data.title}}">
</div>
{{/each}}
</div>
Step 2
Edit your component controller file and add the following code, in our case it is component/input-autocomplete.js
import Component from '@ember/component';
export default Component.extend({
value: '',
currentFocus: -1,
childCount: 0,
filteredResult: null,
lov: null,
init() {
this._super(...arguments);
this.filter('').then((results) => this.set('filteredResult', results));
},
didRender() {
this._super(...arguments);
//this.currentFocus = -1;
this.lov = this.element.children[1];
if (document.activeElement !== this.element.firstChild) {
this.element.children[1].style.display = "none";
}
else
if (this.childCount !== this.element.children[1].children.length) {
this.currentFocus = -1;
this.childCount = this.element.children[1].children.length;
}
},
keyDown: function (evt) {
var charCode = (evt.which != undefined) ? evt.which : event.keyCode;
if (charCode === 40) { //Up Arrow Key
if (this.currentFocus < this.element.children[1].children.length-1)
this.currentFocus++;
else
this.currentFocus = 0;
this.removeHighlight();
this.highlight(this.currentFocus);
} else if (charCode === 38) { //Down Arrow key
if (this.currentFocus > 0)
this.currentFocus--;
else
this.currentFocus = this.element.children[1].children.length-1;
this.removeHighlight();
this.highlight(this.currentFocus);
} else if (charCode == 13) { //Entter Key
this.set('value',this.element.children[1].children[this.currentFocus].children[0].value);
this.removeHighlight();
this.element.children[1].style.display = "none";
this.element.children[0].blur();
}
},
highlight: function(pIndex) {
this.element.children[1].children[pIndex].classList.add("autocomplete-active");
},
removeHighlight: function() {
for (var i =0 ; i < this.element.children[1].children.length ; i++) {
this.element.children[1].children[i].classList.remove("autocomplete-active");
}
},
actions: {
choose(pSelectedText) {
this.set('value',pSelectedText);
this.removeHighlight();
this.element.children[1].style.display= "none";
this.element.children[0].blur();
},
hidedropdown() {
this.element.children[1].style.display = "none";
},
showdropdown() {
this.element.children[1].style.display = "block";
},
search() {
this.element.children[1].style.display = "block";
let filterInputValue = this.value;
let filterAction = this.filter;
filterAction(filterInputValue).then((filterResults) => this.set('filteredResult', filterResults));
}
}
});
Step 3
Edit your parent controller file and copy the following code, in my case it is controller/todo.js
import Controller from '@ember/controller';
export default Controller.extend({
filter:'',
filteredList: null,
actions: {
searchTodo(param) {
if (param !== '') {
return this.store.query('todo', { filter: {title: param } });
} else {
return this.store.findAll('todo');
}
}
}
});
Step 4
Edit your parent view file and copy the following code, in my case it is templates/todo.hbs
<div class="form-group">
<label for="title">myTodo</label>
{{input-autocomplete value=mytodo name="mytodo" placeholder="Enter Todo" id="mytodo" filter=(action 'searchTodo')}}
</div>
Step 5
Start your Ember server with the following command
ember server
Step 6
Run the application from web browser by entering http://localhost:4200/todo focus on the autocmplete field which we created and you will notice a list of todo's appearing from the database and when start typing will notice the list getting filtered, you can use up arrow, down arrow and mouse to navigate in the autocomplete select list.
Note: I am using a Rest based Adapter pointing to nodeJS API with mongoDB. Following code is part of adapter/application.js
Todo Model as Bonus code
Serializer as Bonus code
Hope the code is pretty straight forward and hope it helps you.
Happy Embering :).
Run the application from web browser by entering http://localhost:4200/todo focus on the autocmplete field which we created and you will notice a list of todo's appearing from the database and when start typing will notice the list getting filtered, you can use up arrow, down arrow and mouse to navigate in the autocomplete select list.
Note: I am using a Rest based Adapter pointing to nodeJS API with mongoDB. Following code is part of adapter/application.js
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
host: 'http://localhost:3000'
});
Todo Model as Bonus code
import DS from 'ember-data';
const { Model } = DS;
import { computed } from '@ember/object';
export default Model.extend({
title: DS.attr('string'),
body: DS.attr('string'),
date: DS.attr('date'),
created_at: DS.attr('string', {
defaultValue: function() {
return new Date();
}
}),
isexpired: computed('date',function() {
return new Date() > this.get('date');
})
});
Serializer as Bonus code
import DS from 'ember-data';
import { assign } from '@ember/polyfills';
export default DS.RESTSerializer.extend({
primaryKey: '_id',
serializeIntoHash: function(hash, type, record, options) {
assign(hash, this.serialize(record, options));
}
});
Hope the code is pretty straight forward and hope it helps you.
Happy Embering :).