KnockoutJS computed 不计算(或更新)

KnockoutJS computed does not compute (or update)

在某些情况下,ko.computed 不会更新,即使它绑定到的 ko.observable 发生了变化。我想知道为什么,我做错了什么以及我应该做什么。

例子

考虑这个简单的打到件转换器 (JSFiddle here)。

HTML

<label>Dozen:</label><br>
<input type="number" data-bind="value: dozen">
    <span class="error" data-bind="text: error"></span><br>
<label>Pieces:</label><br>
<input type="number" data-bind="value: pieces"><br>

JavaScript

function ViewModel() {
    var self = this;
    self.error  = ko.observable('');
    self.pieces = ko.observable('');
    self.dozen  = ko.computed({
        read: function() {
            var p = parseInt(self.pieces(), 10);
            if (!p) return '';
            if (p % 12 === 0) return p / 12;
            else return '';
        },
        write: function(value) {
            if (/\D/.test(value)) {
                self.error('Only whole numbers');
            } else {
                self.error('');
                if (value) self.pieces(value * 12);
            }
        }
    });
}

ko.applyBindings(new ViewModel());

它的作用

它将显示两个输入。一个让您输入几十(例如 2),另一个将显示有多少件(24)。

您还可以输入件数(例如 36),转换器将显示 (3) 的数量。

如果您在 pieces 框中输入不能被 12 整除的内容,dozens 输入将被清除,表明该数字不是偶数。同样,我们不允许用户输入小数点。

工作原理

dozens 输入绑定到 KnockoutJS computed observable. It does not hold it's own value (maybe it does under the hood, but this is beyond my knowledge) but is computed from the pieces property, which is a regular observable。为了得到几十个,碎片除以 12,如果结果是整数,那就是 returned,否则我们 return 一个空字符串。

问题

启动计算器并尝试在 dozens 输入中键入 1.5。计算器会通知您不允许使用小数。 pieces 字段将保持不变,因为它应该如此。但是,dozens 输入未被清除以反映 pieces 字段的值。

然后在 pieces 输入中输入一个新值,例如 18 应该 清除 dozens 输入(因为它是 computed 仅取决于视图模型的 pieces 属性,并且此值已更改),但未更改

1.5 一直显示在 dozens 字段中,即使 computedread() 函数永远不会 return 这样的小数. computed绑定的observable更新了,但是computed不计算

只有当您在 pieces 字段中输入可被 12 整除的数字时,dozens 字段才会更新。

我的问题

  1. 为什么 dozens 输入一直显示 1.5,即使 read() 函数的 return 值为空字符串?我可以在 write() 内部或之后强制一个新的 read() 或者强制更新 UI?

  2. 我假设 dozens 输入即使在更改 pieces 输入的值后仍未更新的原因是 read() 函数将连续两次为空字符串,并且 KnockoutJS 会在内部缓存此值并看到它没有更改。同样,如何强制更新输入?

输入一直显示1.5,因为UI和模型不同步。该模型没有收到任何更改,因为该值始终是空字符串。当输入 1.5 时,写入函数中止而不向模型写入任何值,因此就模型而言,这些值仍然为空。然后,当再次为 Pieces 输入 18 时,函数 returns 一个空字符串,如您所料,它没有注册为更改。

您可以强制计算函数用这个 gem 再次更新 UI:computed.notifySubscribers()

write: function(value) {
  if (/\D/.test(value)) {
    self.error('Only whole numbers');
    self.dozen.notifySubscribers(); //<--- here
  } else {
    self.error('');
    if (value) self.pieces(value * 12);
  }
}