Warm tip: This article is reproduced from stackoverflow.com, please click
knockout.js

Knockout

发布于 2020-04-22 11:26:13

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.

Questioner
chut319
Viewed
30
Jason Spake 2020-02-07 07:33

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"/>