计算敲除的循环依赖性

Circular dependency of knockout computed

本文关键字:循环 依赖性 计算      更新时间:2023-09-26

请参阅工作jsFiddle:http://jsfiddle.net/ruslans/vFK82/

我有3个字段:净价(不含税(、税额和总价(价格不含增值税+税额(。NetPrice和Total是可写的,即您可以更改它们中的任何一个,并且其他两个值必须自动计算。

我做这件事的方式是使用3个可观察到的和2个计算出的淘汰对象,但我想也许一个更了解淘汰的人可以提出一种更有效的方法来实现这一点。

html:

Net Price:
<input type="textbox" data-bind="value: NetPriceCalc" />
<br />Tax Amount:
<label data-bind="html: TaxAmt"></label>
<br />Total:
<input type="textbox" data-bind="value: TotalCalc" />

脚本:

var viewModel = {
    NetPrice: ko.observable(100),
    TaxAmt: ko.observable(20),
    Total: ko.observable(120),
    TaxRate: 0.2
};
viewModel.updateTaxAmt = function (useNetPrice) {
    if (useNetPrice) {
        return this.TaxAmt(this.NetPrice() * this.TaxRate);
    } else {
        var total = Number(this.Total());
        var taxAmt = total - total / (1 + this.TaxRate);
        return this.TaxAmt(taxAmt);
    }
};
viewModel.updateNetPrice = function () {
    this.NetPrice(Number(this.Total()) - Number(this.TaxAmt()));
};
viewModel.updateTotal = function () {
    this.Total(Number(this.NetPrice()) + Number(this.TaxAmt()));
};
viewModel.NetPriceCalc = ko.computed({
    read: function () {
        console.log("NetPriceCalc read");
        return viewModel.NetPrice();
    },
    write: function (value) {
        console.log("NetPriceCalc write");
        viewModel.NetPrice(value);
        viewModel.updateTaxAmt(true);
        return viewModel.updateTotal();
    }
});
viewModel.TotalCalc = ko.computed({
    read: function () {
        console.log("TotalCalc read");
        return viewModel.Total();
    },
    write: function (value) {
        console.log("TotalCalc write");
        viewModel.Total(value);
        viewModel.updateTaxAmt(false);
        return viewModel.updateNetPrice();
    }
});
ko.applyBindings(viewModel);

对OP:的一些评论

  • ko.computedwrite方法中不需要return子句
  • 您的方法在多个地方使用Number()函数,您可能希望更改它以获得特定的精度(或用于验证用户输入的某个集中位置(。所以你可以使用ko.extenders来改进它。我特别推荐ko团队已经制作的名为ko.extenders.numeric的扩展器
  • 您的方法还在多个地方使用console.log(),您可能需要使用ko团队ko.extenders.logChange制作的另一个ko.extender
  • 在这种情况下,我认为最好使用subscribe,而不是ko.computed,因为它将占用更少的代码(而且可能不会更快(

我的方法是:

function viewModel() {
    this.TaxRate = 0.2;
    this.NetPrice = ko.observable().extend({ numeric: 2, logChange: "NetPrice"  });
    this.TaxAmt = ko.observable().extend({ numeric: 2, logChange: "TaxAmt"  });
    this.Total = ko.observable().extend({ numeric: 2, logChange: "Total"  });
    this.NetPrice.subscribe(function (newNetPrice) {
        this.TaxAmt(newNetPrice * this.TaxRate);
        this.Total(newNetPrice + this.TaxAmt());
    }, this);
    this.Total.subscribe(function (newTotal) {
        this.TaxAmt(newTotal - newTotal / (1 + this.TaxRate));
        this.NetPrice(newTotal - this.TaxAmt());
    }, this);
    this.NetPrice(100);
}
// then I have the extenders code copied exactly as seen in: http://knockoutjs.com/documentation/extenders.html)
ko.extenders.numeric = ...
ko.extenders.logChange = ... 
// and finally init everything as usual
ko.applyBindings(new viewModel());

你可以在这里看到正在工作的小提琴:http://jsfiddle.net/protron/JFPgu/2/

请注意,此解决方案中的数字的小数位数永远不会超过数字扩展器上指定的小数位数(即使用户输入的值也会自动固定为所需的精度(。

将我的答案与gaurav目前接受的答案进行比较(这也是非常好和简单的(:我认为我的方法的主要优点是它可以让你使用这些很棒的扩展器。

一个稍微好一点且有效的方法可能是:

工作Fiddle

Html

Net Price:
<input type="textbox" data-bind="value: NetPrice" />
<br />Tax Amount:
<label data-bind="html: TaxAmt"></label>
<br />Total:
<input type="textbox" data-bind="value: Total" />

JS

function viewModel() {
    var self = this;
    self.NetPrice = ko.observable(100);
    self.TaxRate = 0.2;
    self.TaxAmt = ko.computed(function() {
        return parseFloat(self.NetPrice()) * self.TaxRate;
    });
    self.Total = ko.computed({
        read: function() { 
               return parseFloat(self.NetPrice()) + self.TaxAmt();
        },
        write: function(val){
                var total = parseFloat(val);
                var taxAmt = total - total / (1 + self.TaxRate);     
                self.NetPrice(total - taxAmt);
        }
    });
}

希望它能有所帮助!

不能保证这会解决问题,但有时启用延迟更新可以解决这样的问题。

这是一个值得注意的好功能,但在为已经运行的应用程序启用它时要小心——而且如果你确实有潜在的问题,那么你仍然需要修复它。

http://knockoutjs.com/documentation/deferred-updates.html