I have two fields on a form; 'Days' and 'Date'.
<div id="myForm">
<label for="days">Days</label>
<input id="days" type="text" data-bind="value:myViewModel.days" />
<label for="date">Date</label>
<input id="date" type="text" data-bind="value:myViewModel.date" />
</div>
Each field is bound to a KO observable. I would like the user to be able to edit either of these fields and the other field to be automatically updated.
My ViewModel is as follows (simplified to keep things concise):
myViewModel = new function () {
var self = this;
self.days = ko.observable(10);
self.date = ko.observable('2020-02-06');
};
myViewModel.days.subscribe(function (newValue) {
var newDate = JSON call to get new date using newValue
myViewModel.date(newDate);
});
myViewModel.date.subscribe(function (newValue) {
var newDays = JSON call to get days using newValue
myViewModel.days(newDays);
});
The problem I'm having is because both values are observables and each updates the other, as soon as one of the values is auto-updated, it then triggers another loop of updates. Is there an alternative approach to get my desired behaviour?
Many thanks in advance.
One way to do this is to bind the UI to a writable computed instead of the observables directly. UI updates get sent to the computed and the computed dictates which underlying observables get updated without triggering any further (model) updates.
function viewModel(){
var self = this;
this._observable1 = ko.observable(10);
this._observable2 = ko.observable(20);
this.observable1 = ko.computed({
read: function(val){
return self._observable1();
},
write: function(val){
self._observable1(val);
self._observable2(val*2); //also update 2
console.log('1 updated');
}
});
this.observable2 = ko.computed({
read: function(val){
return self._observable2();
},
write: function(val){
self._observable2(val);
self._observable1(val / 2); //also update 1
console.log('2 updated');
}
});
}
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
1: <input type="text" data-bind="textInput: observable1"/>
<br/>
2: <input type="text" data-bind="textInput: observable2"/>