see working jsFiddle: http://jsfiddle.net/ruslans/vFK82/
I have 3 fields: Net Price (ex. tax), tax amount and Total price (price ex. vat + tax amount). the NetPrice and the Total are writable, i.e. you can change either of them and the other 2 values must be auto-calculated.
The way I've done it is using 3 observable and 2 computed knockout objects but I thought perhaps someone who knows Knockout a lot better could suggest a more efficient way to achieve this.
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" />
script:
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);
Some comments to the OP:
return
clause in the write
method of the ko.computed
.Number()
function on several places and you might like to change that in order to get an specific precision (or some centralized place for validation of user input). So you can use ko.extenders to improve that. And I would specifically recommend the extender already made by the ko team named ko.extenders.numeric
.console.log()
on several places, and you might want to use another ko.extender made by the ko team ko.extenders.logChange
.ko.computed
in this case I think it's better to use subscribe
as it will take less code (and probably insignificantly faster).My approach would be this:
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());
You can see the working fiddle here: http://jsfiddle.net/protron/JFPgu/2/
Note how the numbers in this solution never have more decimals than the specified on the numeric extenders (even the value entered by the user is automatically fixed to the desired precision).
And comparing my answer to the currently accepted answer by gaurav (which is also pretty nice and simple): I think the main advantage of my approach is that it'll let you use these awesome extenders.
A slightly better and efficient way could be this :
Working 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);
}
});
}
Hope it helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With