I\'m using Bootstrap 2.1.1 and jQuery 1.8.1 and trying to use Typeahead\'s functionality.
I try to display a label and use an id li
I made an Angular 2 directive, typeahead-angular2, that does exactly what you want, and handles the case of non-unique labels as well. You can take the typeahead part.
This directive handle complexe objects with multiple attributes and handle the case where the label isn't unique. it basicly recieves 4 parameters :
@Input() name;
//name for typeahead@Input() objectsDataSet;
// a dataSet of objects , it could be any kind of object@Input() handleFunction;
// a callback function that is called when the object is selected , you can pass the object or whatever you want to this function.@Input() labelAtt;
// the label attribute (object[labelAtt]
is displayed to the user , it must be a string).example :
<input type="text" class="form-control" placeholder="Name..." typeaheadautocomplete [objectsDataSet]="clientList" [labelAtt]="'Firstname'" [name]="'clients'" [handleFunction]="logClient">
as you can see :
clientList
is an array of "client" objects , let's say{"Fistname":"Billel","Lastname":"Guerfa",....}
we use the Firstname attribute for the autocomplete list.logClient
here recieves a client object and displays it.Dependencies :
just declare the typeahead script at the index.html level.
- typeahead : https://twitter.github.io/typeahead.js/
See: https://github.com/BillelGuerfa/typeahead-angular2/
Here is an encapsulated solution. This solution allows you have more than one typeahead on the same page.
This is a modified version of #13279176 Gerbus answer.
$('.make-me-typeahead').typeahead({
source: function (query) {
var self = this;
self.map = {};
var items = [];
var data = [
{"id": 1, "label": "machin"},
{"id": 2, "label": "truc"}
];
$.each(data, function (i, item) {
self.map[item.label] = item;
items.push(item.label)
});
return items;
},
updater: function (item) {
var selectedItem = this.map[item];
this.$element.data('selected', selectedItem);
return item;
}
});
Now when you need get the key of the current selected item you just need do $('.make-me-typeahead').data('selected')
Just another way to implement Pierref function.
var separator = "####";
$("'.autocomplete'").typeahead({
minLength: 3,
source: function (query, process) {
var config = {
type: 'POST',
url: 'Requests/AJAX.PHP', //Change it
cache: 'false',
data: {
query: query
},
dataType: 'json'
};
config.beforeSend = function () {
//TODO : loading gif
};
config.error = function (json) {
if (json.error) {
alert(json.error);
}
};
config.success = function (json) {
if (json.error) {
alert(json.error);
}
var data = [];
for (var i = 0; i < json.data.length; i++) {
data.push(json.data[i].id + separator + json.data[i].name);
}
process(data);
};
$.ajax(config);
},
highlighter: function (item) {
var parts = item.split(separator);
parts.shift();
return parts.join(separator);
},
updater: function (item) {
var parts = item.split(separator);
$('.autocomplete').val(parts.shift());
return parts.join(separador);
}
});
The selected answer doesn't deal with non unique labels (e.g. a person's name). I'm using the following which keeps the default highlighter formatting:
var callback = function(id) {
console.log(id);
};
$('.typeahead',this.el).typeahead({
source: function (query, process) {
var sourceData = [
{id:"abc",label:"Option 1"},
{id:"hfv",label:"Option 2"},
{id:"jkf",label:"Option 3"},
{id:"ds",label:"Option 4"},
{id:"dsfd",label:"Option 5"},
];
var concatSourceData = _.map(sourceData,function(item){
return item.id + "|" + item.label;
});
process(concatSourceData);
},
matcher : function(item) {
return this.__proto__.matcher.call(this,item.split("|")[1]);
},
highlighter: function(item) {
return this.__proto__.highlighter.call(this,item.split("|")[1]);
},
updater: function(item) {
var itemArray = item.split("|");
callback(itemArray[0]);
return this.__proto__.updater.call(this,itemArray[1]);
}
});
To clarify what I was saying in my comment. If you wanted multiple type aheads on the same page you need to define each in a function and create a separate map variable for them.
function initFromField() {
var map;
$('#from:input.autocomplete').typeahead({
source: function(query, process) {
map = {};
var data = [{"id":1,"label":"machin"},{"id":2,"label":"truc"}] // Or get your JSON dynamically and load it into this variable
objects = constructMap(data, map);
process(objects);
},
updater: function(item) {
$('#hidden-from-input').val(map[item].id);
return item;
}
});
}
function initToField() {
var map;
$('#to:input.autocomplete').typeahead({
source: function(query, process) {
objects = [];
map = {};
var data = [{"id":1,"label":"machin"},{"id":2,"label":"truc"}] // Or get your JSON dynamically and load it into this variable
objects = constructMap(data, map);
process(objects);
},
updater: function(item) {
$('#hidden-to-input').val(map[item].id);
return item;
}
});
}
function constructMap(data, map) {
var objects = [];
$.each(data, function(i, object) {
map[object.label] = object;
objects.push(object.label);
});
return objects;
}
$(function initFields() {
initFromField();
initToField();
});
Note how I scoped the map variable inside the two field initialization functions. This is important, it makes sure the same map variable is not used by both input fields.
There's a great tutorial here that explains how to do this: http://tatiyants.com/how-to-use-json-objects-with-twitter-bootstrap-typeahead/ (read my comment on that page if it hasn't been reflected yet in the main part of the post).
Based on that tutorial, and the JSON you provided, you can do something like this:
$(':input.autocomplete').typeahead({
source: function(query, process) {
objects = [];
map = {};
var data = [{"id":1,"label":"machin"},{"id":2,"label":"truc"}] // Or get your JSON dynamically and load it into this variable
$.each(data, function(i, object) {
map[object.label] = object;
objects.push(object.label);
});
process(objects);
},
updater: function(item) {
$('hiddenInputElement').val(map[item].id);
return item;
}
});