简体   繁体   中英

Knockout chained computed observable throws exception on update

I'm using Knockout 3 to build an editable grid rendered as a table. The grid consists of Row objects, one for each row. Inside a Row is an array of Cells (one for each column), and then inside each Cell is one or more Slot objects. The slots are rendered as a list within the table cell.

The outer Row defines a RowIndex observable, the Cells within have a computed observable that reads the row's RowIndex, and the Slots have a computed observable that reads the RowIndex from the Cell.

The problem is that when I set a new RowIndex on a Row, it propagates to the Cell, but when the Cell contains Slots, KO throws an exception: "Uncaught TypeError: Property 'callback' of object [object Object] is not a function" at line 1021 of knockout-3.0.0.debug.js. From Chrome Developer Tools, I see that the subscription's callback property is indeed undefined.

If a Cell does not contain any slots, setting the RowIndex on the Row causes it to propagate to the Cell without a problem.

var Row = function(data) {
  var self = this;

  this.Cells = [];
  this.RowIndex = ko.observable(data.rowIndex);

  // omitted code...
}

var Cell = function(data, row) {
  var self = this;
  this.row = row;

  this.Enabled = ko.observable(true);

  // When row is disabled, disable the cell
  row.Enabled.subscribe(function (value) {
    if (value == false) {
      self.Enabled(false);
    }
  });

  // RowIndex comes from parent row
  this.RowIndex = ko.computed(function () {
    return self.row.RowIndex(); 
  }, this);
  // omitted code ...
}

var Slot = function(data, cell, enabled) {
  var self = this;
  this.cell = cell;

  // RowIndex is cells's RowIndex
  this.RowIndex = ko.computed(function() {
    return self.cell.RowIndex();
  }, this);

  // Any change marks slot as dirty
  self.Title.subscribe(this.updated);
  self.SeatCount.subscribe(this.updated);
  self.RowIndex.subscribe(this.updated);

  this.updated = function() {
    if (self.Action() == actions.None)
    {
      self.Action(actions.Update);
    }
  }

  // When cell is disabled, I feel disabled, too.
  cell.Enabled.subscribe(function(value) {
    if (value == false) {
        self.Enabled(false);
    }
  });

  // omitted code 
}

var ViewModel(colHeadings, rowHeadings, slots) {
  var self = this;

  // Code omitted to iterate the slots and build the rows
  // and cells

  // Invoked on a click to add a row at index
  this.addRow = function(index) {
    var row = new Row({ heading: "New Row", rowIndex: index });
    for (var ci=0; ci<this.columnCount; ci++) {
        var c = new Cell({ colIndex: ci }, row);
        row.Cells.push(c);
    }
    self.RowData.splice(index, 0, row);
    self.renumberRows();
  };

  // Invoked on a click to remove (disable) a row
  this.removeRow = function(row) {
    row.Enabled(false);
    self.renumberRows();
  };

  // Sequentially number the enabled rows
  this.renumberRows = function() {
    var index = 0;
    for (var ri=0; ri<self.RowData().length; ri++) {
      var row = self.RowData()[ri];
      if (row.Enabled()) {
        row.RowIndex(index);
        index++;
      }
    }
  };
}

When I insert a new Row via addRow on the ViewModel, the RowIndex is assigned, and it propagated to the Cells. So that's great. But the new Row's Cells don't contain any Slots yet, so there's no error up to that point. But then I have to renumber the rows so the RowIndex is sequential. As soon as I set a RowIndex on a Row with a Cell that has a Slot, I get the exception. Same goes on removeRow .

The problem is that you've created manual subscriptions with an undefined callback in this section of code from Slot :

// Any change marks slot as dirty
self.Title.subscribe(this.updated);
self.SeatCount.subscribe(this.updated);
self.RowIndex.subscribe(this.updated);

this.updated = function() {
  if (self.Action() == actions.None)
  {
    self.Action(actions.Update);
  }
}

As you can see, you're using this.updated before you've assigned it. So it's undefined then.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM