Harris
Harris

Reputation: 1138

knockout foreach binding with a function

Consider the following code:

<div class="expandedCoverages" data-bind="foreach: $root.getGroupCoveragesPerPackage(packageCost)">
  <div class="calculateCostCoverage" 
         data-bind="html: CoverageGroupDescription()"></div>
  </div>
</div>

If packageCost observable changes, CoverageGroupDescription() doesn't change since function isn't triggered again.

Is it possible somehow to trigger again the function so as to change the foreach binding?

Upvotes: 0

Views: 3531

Answers (1)

webketje
webketje

Reputation: 10966

Under the hood this is what Knockout does with functions/ statements executed directly in the view (HTML):

  • If the code does not depend on an observable, it will be evaluated only once.
  • If the code depends on an observable, it will create a computed observable that will re-evaluate every time that observable changes.

Although using a computed property in the JS model, like Sam C.'s comment suggests, is cleaner, it is not absolutely necessary. Doing:

<div data-bind="foreach: myFunction(observableParam())"></div>

is actually the same as doing:

<div data-bind="foreach: myComputed"></div>

with:

this.myComputed = ko.computed(function() { return myFunction(observableParam()); });

That means in this case probably coverageGroupDescription does not return a correctly updated array. See a working example with a more or less similar implementation as in the question, below. Change the packageCost and the descriptions will automatically change too.

var Coverage = function(cost) {
  this.cost = ko.observable(cost);
  this.coverageGroupDescription = function() {
    return 'This package costs ' + this.cost() + '$';
  }.bind(this);
}

var app = {
  items: ko.observableArray([
    new Coverage(15),
    new Coverage(20),
    new Coverage(25),
    new Coverage(30),
    new Coverage(35),
  ]),
  packageCost: ko.observable(1),
  getGroupCoveragesPerPackage: function(m) {
    var m = m();
    return ko.utils.arrayMap(this.items(), function(item, i) {
      return new Coverage(item.cost()*m);
    });
  }
};

ko.applyBindings(app);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

Multiply all by: <input type="text" data-bind="value: packageCost">
<ul data-bind="foreach: getGroupCoveragesPerPackage(packageCost)">
  <li data-bind="text: coverageGroupDescription()"></li>
</ul>

Upvotes: 2

Related Questions