Reputation: 12431
I have a page with a select and an input-box being bound to the same value. The idea is that normally one would select a value from the select, however, the user should also be able to enter an arbitrary string in the input-box. The problem is that if I enter something not present in the select, because of the binding, the value is set to the first item in the select.
This is the behavior I want to achieve:
User selects value from select
User enters text in input
In other words, what I want is for the last changed control to be the valid Value. But I also want both controls to be up to date as long as a given value is valid for that control.
My code looks like this:
js
var viewModel = { Value: ko.observable('1'), Set: ['1', '2', '3'] };
ko.applyBindings(viewModel);
html
<!-- ko if: Set.length > 1 || (Set.length > 0 && Set[0] != '') -->
<select type="text" class="form-control input-small" data-bind="options: Set, value: Value">
</select>
<!-- /ko -->
<input class="form-control input-small" data-bind="value: Value" style="margin-top: 5px;" />
Here is a jsfiddle showing how the code currently works: http://jsfiddle.net/b2RwG/
[Edit]
I've found a working solution (http://jsfiddle.net/b2RwG/2/), however it's really not pretty, and there has to be a better way to solve this problem.
Upvotes: 2
Views: 1934
Reputation: 416
You can have the select use a computed observable instead, which updates only if the value makes sense.
I made an example where i added a caption to the select. The result is that it doesn't automatically pick the first value, but instead tries to set undefined value, when it reads a value that isn't included in the Set
array.
<select type="text" class="form-control input-small" data-bind="options: Set, value: SelectValue, optionsCaption: 'Other value'"></select>
To do that, a constructor function instead of an object literal will make it easier, because then you can access the Value
observable through the self
reference.
function ViewModel() {
var self=this;
this.Value = ko.observable('1');
this.Set = ['1', '2', '3'];
this.SelectValue= ko.computed({
read: function() {
var val = self.Value();
return val;
},
write: function(value) {
if(value) self.Value(value);
}
});
}
See http://jsfiddle.net/b2RwG/4/
Upvotes: 0
Reputation: 8987
As you can see I add an inputValue observable that is bound to the text input. I also add an computed named virtualSet that contains both original items and the new item (from the text input). I susbcribe to the inputValue so the select will be automatically set when you are typing.
var viewModel = {
inputValue: ko.observable('1'),
Value: ko.observable('1'),
Set: ['1', '2', '3']
};
viewModel.virtualSet = ko.computed({
read: function () {
var vs = this.Set.slice(0);
if (this.inputValue() && this.inputValue().length)
vs.unshift(this.inputValue());
return vs;
},
owner: viewModel
});
viewModel.inputValue.subscribe(function (value) {
viewModel.Value(value);
});
I hope it helps.
Upvotes: 1