HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Two Knockout computed dependent on each other

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
computedeachknockouttwootherdependent

Problem

I have 3 fields:

  • Net Price (ex. tax)



  • tax amount



  • 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.

Working jsFiddle

HTML:

Net Price:

Tax Amount:

Total:


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);

Solution

I will review and contrast the SO answer that I liked best:

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);
        }
    });
}


  • It is more standard to create a constructor, then to create an object with object notation and then add functions.



  • Not to big a fan of using self everywhere instead of this in the SO answer



  • Do not use console.log in production code



  • Do not use calc as part of the variable name



  • Do not use a useNetPrice type of indicator, knockout can figure out the computed values on its own and knows when to recalculate what



  • write functions do not have to return anything

Code Snippets

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);
        }
    });
}

Context

StackExchange Code Review Q#24925, answer score: 6

Revisions (0)

No revisions yet.