Javascript 敲除计算的循环依赖性

Javascript 敲除计算的循环依赖性,javascript,knockout.js,Javascript,Knockout.js,请参阅工作JSFIDLE: 我有3个字段:净价(不含税)、税额和总价(不含增值税的价格+税额)。 净价和总价是可写的,也就是说,您可以更改其中任何一个,其他两个值必须自动计算 我这样做的方式是使用3个可观察的和2个计算的敲除对象,但我想也许对敲除更了解的人可以提出一种更有效的方法来实现这一点 html: 一个稍微好一点且有效的方法可以是: Html 希望有帮助 对OP的一些评论: 您不需要ko.computed的write方法中的return子句 您的方法在多个位置使用Number()函数,

请参阅工作JSFIDLE:

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

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

html:


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

Html


希望有帮助

对OP的一些评论:

  • 您不需要
    ko.computed
    write
    方法中的
    return
    子句
  • 您的方法在多个位置使用
    Number()
    函数,您可能希望更改该函数,以获得特定的精度(或用于验证用户输入的某个集中位置)。所以你可以用它来改进。我特别推荐ko团队已经生产的扩展器
  • 您的方法还在多个地方使用
    console.log()
    ,您可能希望使用另一个由ko团队制造的ko.extender
  • 在这种情况下,我认为最好使用
    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());
您可以在这里看到工作小提琴:

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


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

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

这是一个需要注意的好特性,但是在为已经运行的应用程序启用它时要谨慎,而且如果您确实有一个潜在的问题,那么您仍然需要修复它


非常酷。您可以将
valueUpdate:'afterkeydown'
添加到字段的数据绑定属性中,以便它们在每次按键时都会更新。噢,
extender
非常酷,特别是我可以将子观察值添加到现有观察值中这一事实-我的用例是关于渲染UI的特定部分的错误消息,来自不同的端点(不是我选择的API体系结构)。注意:这与延迟可观察性不同,是3.4.0以后的新特性
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);
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" />
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);
        }
    });
}
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());