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: