【问题标题】:Knockout chained computed observable throws exception on updateKnockout 链式计算 observable 在更新时抛出异常
【发布时间】:2014-01-31 02:48:44
【问题描述】:

我正在使用 Knockout 3 构建呈现为表格的可编辑网格。网格由 Row 对象组成,每行一个。 Row 内部是一个 Cells 数组(每列一个),然后每个 Cell 内部是一个或多个 Slot 对象。插槽在表格单元格中呈现为列表。

外层 Row 定义了一个 RowIndex observable,其中的 Cells 有一个计算出的 observable,它读取行的 RowIndex,而 Slots 有一个计算出的 observable,它从 Cell 中读取 RowIndex。

问题是当我在Row上设置一个新的RowIndex时,它会传播到Cell,但是当Cell包含Slots时,KO抛出异常:“Uncaught TypeError: Property 'callback' of object [object Object] is not a function”在 knockout-3.0.0.debug.js 的第 1021 行。从 Chrome 开发者工具中,我看到订阅的 callback 属性确实是未定义的。

如果 Cell 不包含任何槽,则在 Row 上设置 RowIndex 会导致它毫无问题地传播到 Cell。

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++;
      }
    }
  };
}

当我通过 addRow 在 ViewModel 上插入新行时,会分配 RowIndex,并将其传播到单元格。那太好了。但是新的 Row's Cells 还没有包含任何 Slots,所以到目前为止没有错误。但随后我必须重新编号行,以便 RowIndex 是连续的。一旦我在具有插槽的单元格的行上设置行索引,我就会得到异常。 removeRow 也是如此。

【问题讨论】:

  • 如果 callback 未定义,这意味着 subscribe 在您的代码中的某处被调用,而没有提供任何“回调”。
  • 嗯。我在几个地方使用订阅,但我每次都提供一个回调函数。我将编辑代码以包含它们。
  • 谢谢。这正是我需要给你答案的。

标签: javascript knockout.js


【解决方案1】:

问题是您在来自Slot 的这部分代码中使用undefined 回调创建了手动订阅:

// 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);
  }
}

如您所见,您在分配之前使用了this.updated。所以是undefined

【讨论】:

  • 我的额头现在有一个手掌状的红色印记。我整天盯着那段代码,没有发现任何问题。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-24
  • 1970-01-01
相关资源
最近更新 更多