I am trying to create a lazy loading select with knockout js. Loading the data on demand and getting it to update the UI is simple thanks to knockout. However I have an issue with setting the correct item once the data has been loaded.
Example jsFiddle here
After a bit of digging it appears that knockout overrides the value because it wants to match the model to the options. See 'ensureDropdownSelectionIsConsistentWithModelValue' in knockout-2.2.1.debug.js.
This is a problem for me because at the point it does this, there are no options so it overrides my model value with 0.
I'm fairly new to knockout so I suspect I am approaching this wrong... Has anyone achieved this? or have a better approach to suggest?
I have read Ryan's blog post Lazy Loading an Observable in KnockoutJS but I cannot see a way to leverage his approach that would solve my issue.
Thanks in advance for any help :)
Ste.
Code Sample:
Html:
<select data-bind="options: $data.choice.options, optionsText: 'text', optionsValue: 'value', value: $data.choice"></select>
JS:
var optionsProvider = (function () {
"use strict";
var self = {};
//container for options data, a sort of dictionary of option arrays.
self.options = {};
self.init = function (optionData) {
//pre-populate any provided options data here...
};
self.get = function(name) {
if (!self.options[name]) {
self.options[name] = ko.observable([]);
//ajax request for options
//populate self.options[name] with options upon return
//dummy this with below for example.
setTimeout(function() {
self.options[name]([
{ text : "option1", value : 1 },
{ text : "option2", value : 2 },
{ text : "option3", value : 3 },
]);
}, 1000); //simulate some delay
}
//return reference to observable immediately.
return self.options[name];
};
return self;
})();
var simpleModel = function() {
this.choice = ko.observable(2); //hard code selected option to simulated pre-saved selection.
this.choice.options = optionsProvider.get("SomeOptionType");
};
ko.applyBindings(new simpleModel());
I am trying to create a lazy loading select with knockout js. Loading the data on demand and getting it to update the UI is simple thanks to knockout. However I have an issue with setting the correct item once the data has been loaded.
Example jsFiddle here
After a bit of digging it appears that knockout overrides the value because it wants to match the model to the options. See 'ensureDropdownSelectionIsConsistentWithModelValue' in knockout-2.2.1.debug.js.
This is a problem for me because at the point it does this, there are no options so it overrides my model value with 0.
I'm fairly new to knockout so I suspect I am approaching this wrong... Has anyone achieved this? or have a better approach to suggest?
I have read Ryan's blog post Lazy Loading an Observable in KnockoutJS but I cannot see a way to leverage his approach that would solve my issue.
Thanks in advance for any help :)
Ste.
Code Sample:
Html:
<select data-bind="options: $data.choice.options, optionsText: 'text', optionsValue: 'value', value: $data.choice"></select>
JS:
var optionsProvider = (function () {
"use strict";
var self = {};
//container for options data, a sort of dictionary of option arrays.
self.options = {};
self.init = function (optionData) {
//pre-populate any provided options data here...
};
self.get = function(name) {
if (!self.options[name]) {
self.options[name] = ko.observable([]);
//ajax request for options
//populate self.options[name] with options upon return
//dummy this with below for example.
setTimeout(function() {
self.options[name]([
{ text : "option1", value : 1 },
{ text : "option2", value : 2 },
{ text : "option3", value : 3 },
]);
}, 1000); //simulate some delay
}
//return reference to observable immediately.
return self.options[name];
};
return self;
})();
var simpleModel = function() {
this.choice = ko.observable(2); //hard code selected option to simulated pre-saved selection.
this.choice.options = optionsProvider.get("SomeOptionType");
};
ko.applyBindings(new simpleModel());
Share
Improve this question
asked Apr 17, 2013 at 15:12
Ste WSte W
1331 silver badge8 bronze badges
3 Answers
Reset to default 3Generally, how I have handled this situation is to pre-populate the observableArray with the current value as its only item.
For you, you could potentially accept an initialValue in your getter like:
self.get = function(name, initialValue) {
if (!self.options[name]) {
self.options[name] = ko.observableArray([{ value: initialValue }]);
//ajax request for options
//populate self.options[name] with options upon return
//dummy this with below for example.
setTimeout(function() {
self.options[name]([
{ text : "option1", value : 1 },
{ text : "option2", value : 2 },
{ text : "option3", value : 3 },
]);
}, 1000); //simulate some delay
}
//return reference to observable immediately.
return self.options[name];
};
and pass it through like:
var simpleModel = function() {
var initialValue = 2;
this.choice = ko.observable(initialValue); //hard code selected option to simulated pre-saved selection.
this.choice.options = optionsProvider.get("SomeOptionType", initialValue);
};
Here is a sample: http://jsfiddle/rniemeyer/sHB9p/
For a slightly more generic way, I have used a custom binding to make this happen something like:
ko.bindingHandlers.populateInitialValue = {
init: function(element, valueAccessor, allBindingsAccessor) {
var bindings = allBindingsAccessor(),
options = ko.utils.unwrapObservable(bindings.options),
optionsValue = bindings.optionsValue,
value = ko.utils.unwrapObservable(bindings.value),
initialValue;
if (options && !options.length) {
if (optionsValue) {
initialValue = {};
initialValue[optionsValue] = value;
}
else {
initialValue = value;
}
bindings.options.push(initialValue);
}
}
};
Then, use it (with no other changes to your code):
<select data-bind="populateInitialValue: true, options: $data.choice.options, optionsText: 'text', optionsValue: 'value', value: $data.choice"></select>
This just looks at the other options and builds an initial value.
Sample: http://jsfiddle/rniemeyer/SmSC6/
This is a wicked old question but I thought I would post for the benefit of anyone else who es along and finds this. I have written a plugin to encapsulate the options available to a select object along with its selected index.
This plugin also solves the original problem described above because you are able to load a value into the observable and then load the options in later and provided that value exists in the list that is loaded in then the observable will automatically hook this up.
Take a look, hope this helps anyone ing by all these years later :)
https://github./ozrevulsion/KoSelectSugar
Here is an updated fiddle http://jsfiddle/sujesharukil/5YFry/5/
You can subscribe to changes to the options and then set the value, a simple approach that way.
var simpleModel = function() {
var self = this;
this.choice = ko.observable(2); //hard code selected option to simulated pre-saved selection.
this.choice.options = optionsProvider.get("SomeOptionType");
this.choice.options.subscribe(function(newValue){
self.choice(2);
});
};