Skip to content

Instantly share code, notes, and snippets.

@dkotov
Last active December 15, 2015 09:19
Show Gist options
  • Save dkotov/5237125 to your computer and use it in GitHub Desktop.
Save dkotov/5237125 to your computer and use it in GitHub Desktop.
Twitter Bootstrap Typeahead wrapper that uses objects instead of strings (lite version) -- see example http://jsfiddle.net/VjVMf/.
<input type="text" data-bind="value: customerAC.query, jqueryui: { widget: 'typeahead', options: { source: customerAC.source } }" placeholder="Start typing customer name..." />
<br />
<strong data-bind="text: selectedCustomerId"></strong>
function KnockoutAutocomplete(baseUrl, observableValue, filterCondition) {
var self = this;
self.selectedItem = ko.observable(null);
self._itemsMap = {};
self.updateQuery = ko.observable(false);
var observe = typeof observableValue === 'function';
if (!baseUrl.match(/\/$/)) baseUrl += "/";
self.search = function(query, key, process) {
ko.utils.get(baseUrl + 'autocomplete', { query: query, key : key }, function (data) {
// filter
var filteredItems = data;
if (typeof filterCondition === 'function') {
filteredItems = [];
$.each(data, function (i, item) {
if (filterCondition(item))
filteredItems.push(item);
});
}
process(filteredItems);
});
};
self.query = ko.computed({
read: function () {
self.updateQuery(); // force update in case if self.selectedItem wasn't changed
return self.selectedItem() ? self.selectedItem().Text : null;
},
write: function (title) {
var item = title && (!filterCondition || filterCondition(title))
? self._itemsMap[title.toLowerCase()]
: null;
self.selectedItem(item);
self.updateQuery(!self.updateQuery());
if (observe) {
var newValue = item ? item.Value : null;
observableValue(newValue);
}
}
});
self.source = function (query, process) {
self.search(query, null, function (items) {
// map
var titles = [];
$.each(items, function (i, item) {
self._itemsMap[item.Text.toLowerCase()] = item;
titles.push(item.Text);
});
// display
process(titles);
});
};
self.selectByValue = function (val) {
if (!val) {
self.selectedItem(null);
return;
}
// try find within saved items list
for (var title in self._itemsMap) {
var item = self._itemsMap[title];
if (item && item.Value == val) {
self.selectedItem(item);
return;
}
}
// else retrieve from server
self.search(null, val, function (items) {
if (items.length > 0) {
var itemForSelection = items[0];
self._itemsMap[itemForSelection.Text.toLowerCase()] = itemForSelection;
self.selectedItem(itemForSelection);
}
});
};
if (observe) {
observableValue.subscribe(function(newValue) {
self.selectByValue(newValue);
});
self.selectByValue(observableValue());
}
}
var model = {
selectedCustomerId: ko.observable()
};
model.customerAC = new KnockoutAutocomplete('/customers/', model.selectedCustomerId);
$(document).ready(function() {
ko.applyBindings(model);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment