SeanW
SeanW

Reputation: 33

knockoutjs: order matters in computed observable?

I have a computed observable that does not update due to the order of the observables in the computed observable function. Clicking on the branch does not update the computed observable on the leaf. Is this intended?

https://jsfiddle.net/acL3f1qp/9/

javascript:

(function() {

  function Branch(viewModel) {
    var self = this;
    self.isAllowed;
    self.isChecked = ko.observable();
    self.isChecked.subscribe(function(newValue){
        //is updating?
      null;
    });
    self.leaves = [];
    self.allowLeaves = ko.computed(function() {
      return viewModel.allowLeaves() && (!self.isAllowed || self.isChecked());
    });
  }

  function Leaf(branch) {
    var self = this;
    var isCheckedInternal = ko.observable();
    self.isAllowed;
    self.isChecked = ko.computed({
            read: function() {
            return branch.allowLeaves() && isCheckedInternal();
            },
        write: function(value) {
            isCheckedInternal(value);
        }
    });
  }

  function ViewModel() {
    var self = this;
    var branch;
    var leaf;
    self.allowLeaves = ko.observable(true);
    self.branches = [];
    branch = new Branch(self);
    branch.isAllowed = true;
    branch.isChecked(true);
    leaf = new Leaf(branch);
    leaf.isAllowed = true;
    leaf.isChecked(true);
    branch.leaves.push(leaf);
    self.branches.push(branch);
  }

  ko.applyBindings(new ViewModel());

})();

html:

<div>
  <label>
    <input type="checkbox" data-bind="checked: allowLeaves" />
    <span>allow leaves</span>
  </label>
</div>
<div class="indent" data-bind="foreach: branches">
  <div>
    <label>
      <input type="checkbox" data-bind="checked: isChecked, visible: isAllowed" />
      <span>branch</span>
    </label>
  </div>
  <div class="indent" data-bind="foreach: leaves">
    <div>
      <label>
        <input type="checkbox" data-bind="checked: isChecked, visible: isAllowed" />
        <span>leaf</span>
      </label>
    </div>
  </div>
</div>
<br />
clicking on "branch" does 

not update computed on leaf!

Upvotes: 3

Views: 506

Answers (1)

jaybeeuu
jaybeeuu

Reputation: 1123

The order of observables in a computed only matters as far as building dependencies;

In this computed:

ko.pureComputed(function(){
  return a() && b(); 
}

(assume a and b are observable and return either true or false)

If a returns false then b will not be evaluated (since the && operator is lazy) and KO will not create a dependency on b, only on a.

This is good - any change to b can have no impact on the value of this computed so reevaluating would be a waste of resources.

If a is updated to return true KO will reevaluate the computed (since it does have a dependency on a) and in doing so will need to find out what value b has and will then create a dependency.


As for your fiddle - as far I can see your problem is that the isChecked observable on the branch is not linked in any way to the allowLeaves computed. So it doesn't get updated.

If you change you implementation of Branch to look more like Leaf with an isCheckedInternal like this:

function Branch(viewModel) {
  var self = this;
  self.isAllowed = ko.observable();
  var isCheckedInternal = ko.observable();

  self.leaves = [];
  self.allowLeaves = ko.pureComputed({
    read: function() {
      return viewModel.allowLeaves() && isCheckedInternal();
    },
    write: function(val){
      isCheckedInternal(val);
  }});
}

and then bind the checked to allowLeaves then it appears to work as expected.

I have a fiddle which is working...

Upvotes: 2

Related Questions