I\'m generating a datalist options based on a ko observable.
function Company(company) {
this.id = company.Id;
this.name = company.Name;
I want to display the name property, but I need to store the Id.
Since there is currently no Knockout binding for HTML5 , I made one.
I tried to borrow as much as I could from the select binding, so there is support for options, optionsText and optionsValue. You can specify a target observable via value.
ko.bindingHandlers.datalist = (function () {
function getVal(rawItem, prop) {
var item = ko.unwrap(rawItem);
return item && prop ? ko.unwrap(item[prop]) : item;
}
function findItem(options, prop, ref) {
return ko.utils.arrayFirst(options, function (item) {
return ref === getVal(item, prop);
});
}
return {
init: function (element, valueAccessor, allBindingsAccessor) {
var setup = valueAccessor(),
textProperty = ko.unwrap(setup.optionsText),
valueProperty = ko.unwrap(setup.optionsValue),
dataItems = ko.unwrap(setup.options),
myValue = setup.value,
koValue = allBindingsAccessor().value,
datalist = document.createElement("DATALIST");
// create an associated
I hope I got most of this right. Corrections and improvement suggestions are welcome.
You would use it like this:
Notes.
in the HTML. The custom binding does that for you.list="" attribute of the element. I found no way of dynamically setting that in JavaScript so far.value is optional, but if you supply it, it must be an observable.value binding outside of datalist. It will contain whatever text the displays (no surprises there). However, writing to the built-in value also updates the datalist value, and the other way around.datalist value has precedence and will overwrite the built-in value upon view model init. After that they stay in sync.options should be an array (plain old or observable) of objects — companies in this case).optionsText and optionsValue are strings that should map to properties on your options.optionsValue — in that case the entire object (a single company) would be stored in value. I would prefer that over storing only the ID.change event. That means your view model won't update until you leave the input field.Below is a demo, copied from the original fiddle.
function main() {
function Company(company) {
this.id = company.Id;
this.name = company.Name;
this.state = company.State.name;
}
function ViewModel(sampleData) {
var self = this;
self.companies = ko.observableArray();
ko.utils.arrayForEach(sampleData, function (companyData) {
self.companies.push(new Company(companyData));
});
// KO's "value" binding _can_ supply a start value
self.val = ko.observable("Microsoft");
// ... but it is overridden by datalist's "value" binding - in any case you can use both
self.selectedCompany = ko.observable(1);
}
// -----------------------------------------------------------------------------------
ko.applyBindings(new ViewModel([{
Id: 1,
Name: "Apple",
State: {
name: "California"
}
}, {
Id: 2,
Name: "Microsoft",
State: {
name: "Washington"
}
}, {
Id: 3,
Name: "IBM",
State: {
name: "New York"
}
}]));
}
// -----------------------------------------------------------------------------------
ko.bindingHandlers.datalist = (function () {
function getVal(rawItem, prop) {
var item = ko.unwrap(rawItem);
return item && prop ? ko.unwrap(item[prop]) : item;
}
function findItem(options, prop, ref) {
return ko.utils.arrayFirst(options, function (item) {
return ref === getVal(item, prop);
});
}
return {
init: function (element, valueAccessor, allBindingsAccessor) {
var setup = valueAccessor(),
textProperty = ko.unwrap(setup.optionsText),
valueProperty = ko.unwrap(setup.optionsValue),
dataItems = ko.unwrap(setup.options),
myValue = setup.value,
koValue = allBindingsAccessor().value,
datalist = document.createElement("DATALIST");
// create an associated
.hint {
color: silver;
font-size: 80%;
}
(note the "change" event occurs after the field loses focus!)
View Model: