Search code examples
knockout.jscomputed-observable

knockout.js - when do computed items recalculate dependencies?


Suppose you have the following js:

  var vm = {
    objects: ko.observable([]),
    objectCount: ko.computed(function(){
      return vm && vm.objects().length;
    }),

    add: function() {
      var current = vm.objects();
      current.push({});      
      console.log('current is', current);
      vm.objects(current);
      console.log("should recalculate here", vm.objectCount());
    }
  };

And the following html:

  <ul data-bind="foreach: objects">
    <li>
      Object: <span data-bind="text: $index"></span>
    </li>
  </ul>
  <button data-bind="click: add">Add Object</button>
  <p>
    Total number of objects:
    <span data-bind="text: objectCount"></span>
  </p>

It is my understanding from reading the documentation that because after I add an object I call objectCount(), that it should recalculate its dependencies. Instead it seems to never even execute the function, running it only once!

JSBin demonstrating a simplified version of my issue.


Solution

  • It's just a scoping issue. If you refactor your script code to look like this:

    $(function(){
      var vm = function(){
        var self = this;
    
        self.objects = ko.observable([]);
        self.objectCount = ko.computed(function(){
          return self.objects().length;
        });
    
        self.add = function() {
          var current = self.objects();
          current.push({});      
          console.log('current is', current);
          self.objects(current);
          console.log("should recalculate here", self.objectCount());
        };
      };
    
      ko.applyBindings(new vm());
    });
    

    Then the correct variables are scoped and Knockout correctly calculates the dependencies. As stated by guigouz, when ko.computed is initially called, vm is undefined, so it cannot setup any change handlers.

    Here is the updated JSBin