MojoDK
MojoDK

Reputation: 4528

How to compute a knockout observableArray from another viewmodel?

I'm learning knockoutjs, so please bear with me...

Take this code:

HTML:

<div id="itemsContainer">
</div>
<div id="cartContainer">
  <label data-bind="text: totals"></label>
</div>
<div id="items"></div>

Javacript:

function ItemsViewModel() {
  var self = this;
  self.items = ko.observableArray().publishOn("items");
  self.items.push({
    count: 2,
    price: 100
  });
  self.items.push({
    count: 3,
    price: 200
  });
}

function CartViewModel() {
  var self = this;
  self.totals = ko.computed(function() {
    var total = 0;
    $.each(self, function(i, m) {
      total = total + (m.count * m.price);
    });
    return total;
  }, this).subscribeTo("items", true);

}

var itemsVM;
var cartVM;

itemsVM = new ItemsViewModel();
ko.applyBindings(itemsVM, document.getElementById("itemsContainer"));

cartVM = new CartViewModel();
ko.applyBindings(cartVM, document.getElementById("cartContainer"));

Fiddle

I want to update the "totals" depending on the data I put into (or change) in the ItemsViewModel.items.

I'm stuck now and have no idea how to make it work?

Upvotes: 4

Views: 125

Answers (1)

user3297291
user3297291

Reputation: 23372

I'm not sure if subscribeTo works on a computed like you've tried... A quick fix would be to create a (private) mirror inside the CartViewModel constructor and use that in your computed:

function CartViewModel() {
  var self = this;

  var allItems = ko.observableArray([]).subscribeTo("items", true);

  self.totals = ko.computed(function() {
    return allItems().reduce(function(total, m) {
      return total + (m.count * m.price);
      }, 0);
  });
}

Note: I've replaced your $.each with an Array.prototype.reduce ;)


Edit: I've found another answer in the docs: you can use a transform function:

function CartViewModel() {
  var self = this;

  self.totals = ko.observableArray([])
    .subscribeTo("items", true, function(items) {
      return items.reduce(function(total, m) {
       return total + (m.count * m.price);
    }, 0);
  });
};

Updated fiddle with mirror approach: http://jsfiddle.net/qzLkjLL1/

Updated fiddle with transform approach: http://jsfiddle.net/ynoc6hha/

Upvotes: 3

Related Questions