Knockout.js中的相互递归字段

时间:2013-06-06 09:47:17

标签: knockout.js

我想解决knockout.js中的以下问题:我有三个输入;我需要设置的pricequantitytotal

 total = price * quantity 

我更新pricequantity时,我也想设置

 price = total / quantity 

如果我更新total

我最初使用计算的observable来做这个,但是这几乎只能工作,但是我有一个舍入问题。例如。如果我做

quantity(3)
total(100)

我希望price设置为33.33,只有两位小数,而total仍应设置为100,因此标识total = price * quantity并不完全成立。

在我采用的计算可观察方法中,total在这种情况下将设置为99.99。 这就是使这种情况与文档中的fullname / firstname / lastname示例不同的原因。

基本上price / total字段是相互递归的。除了手动订阅之外,我找不到解决这个问题的好方法,但这感觉非常单一,并且意味着我必须手动处理一堆订阅等。

2 个答案:

答案 0 :(得分:3)

我通过制作“真正有价值”的ko.observables(即数量,价格和总数),在表单字段上跟踪焦点事件,并使用ko.computed()重新计算(但不是如果选择了当前字段)并格式化表单的值。

一句话描述听起来很复杂,但代码并不复杂。

JavaScript的:

var Model = function () {
    var self = this;

    self.quantity = ko.observable(0);
    self.price = ko.observable(0);
    self.price.selected = ko.observable(false);
    self.total = ko.observable(0);
    self.total.selected = ko.observable(false);

    self.formattedPrice = ko.computed({
        read: function () {
            if (!self.price.selected()) {
                 var total = self.total() || 0;
                 var quantity = self.quantity() || 0;
                 var value = total / quantity;
                 self.price(isNaN(value) ? 0 : value);
            }
            return '$' + self.price().toFixed(2);
        },
        write: function (value) {
            value = parseFloat(value.replace(/[^\.\d]/g, ""));
            self.price(isNaN(value) ? 0 : value);
        }
    });

    self.formattedTotal = ko.computed({
        read: function () {
            if (!self.total.selected()) {
                var quantity = self.quantity() || 0;
                var price = self.price() || 0;
                var value = quantity * price;
                self.total(isNaN(value) ? 0 : value);
            }
            return '$' + self.total().toFixed(2);
        }, 
        write: function (value) {
            value = parseFloat(value.replace(/[^\.\d]/g, ""));
            self.total(isNaN(value) ? 0 : value);
        }
    });
};

ko.applyBindings(new Model());

在HTML中,您可以像这样绑定价格和总数:

<input data-bind="value: formattedPrice, hasfocus: price.selected" />

<input data-bind="value: formattedTotal, hasfocus: total.selected" />

jsfiddle处的代码,如果您想查看一个有效的示例。

答案 1 :(得分:0)

感谢您的回答。仅供参考,这是我目前的解决方案,可能会坚持下去,除非它给我带来麻烦。

    var self = this;
    this.price = ko.observable().extend({numeric: 2});
    this.quantity = ko.observable(1).extend({ numeric: 2 });
    this.total = ko.observable(1).extend({ numeric: 2 });

    var unsub_total = this.total.subscribe(function (total) {
        if (self.quantity() != 0)
            self.price(total / self.quantity())
    });

    // setting quantity will update both price and total
    var unsub_qty = this.quantity.subscribe(function (qty) {
        self.total(self.price() * qty);
    });

    var unsub_price = this.price.subscribe(function (price) {
        self.total(price * self.quantity());
    });

    this.unsub = function() { unsub_total(); unsub_price(); unsub_qty(); };