Tax and checkboxes

http://stackoverflow.com/questions/13988233/knockout-js-foreach-nested-value-updates-in-all-fields/14027090#14027090

by MizardX

HTML

<div data-bind="foreach: TaxRate().ServicesRates">
    <h1 data-bind="text: TaxTypeName"></h1>
    <label>
        <input type="checkbox" data-bind="checked: ApplyForAllCheckBox" />
        Apply for All Services
    </label>
    <input type="text" data-bind="value: ApplyForAll" /> %
    <div data-bind="foreach: Classes">
        <h2 data-bind="text: TaxClass().ServiceClassName"></h2>
        <table>
            <thead>
                <tr>
                    <th>Service Name</th>
                    <th>Amount</th>
                </tr>
            </thead>
            <tbody  data-bind="foreach: ClassServices">
                <tr>
                    <td data-bind="text: TaxService().ServiceName"></td>
                    <td>
                        <input type="text" data-bind="value: ServiceRate" />%
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</div>

<pre data-bind="text: ko.toJSON(TaxRate(),null,2)"></pre>

CSS

html, body { font-family: serif; }
h1, h2, h3, h4, h5, h6 { font-family: sans-serif; font-weight: bold; margin-top: 1em; }
div, table { margin-left: 1em; }
td, th { padding: 0 1em; }
th, caption { font-weight: bold; }

JavaScript

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

    self.TaxRateID = ko.observable(-1);
    self.ServicesRates = ko.observableArray([]);
};

var ServiceLevelRate = function(data, availableClasses, availableServices) {
    var self = this;

    self.TaxTypeID = ko.observable(data.Key);
    self.TaxTypeName = ko.observable(data.Name);
    self.ExtendedTaxTypeName = ko.computed(function () {
        return self.TaxTypeName().replace(/\s+/g, '');
    });
    self.ApplyForAllCheckBox = ko.observable(false);
    self.ApplyForAll = ko.observable("");
    self.Classes = ko.observableArray(
        ko.utils.arrayMap(availableClasses, function (cls) {
            return new TaxClassMapping(cls, availableServices);
        })
    );

    self.ApplyForAll.subscribe(function (newValue) {
        if (self.ApplyForAllCheckBox()) {
            ko.utils.arrayForEach(self.Classes(), function (clsMapping) {
                ko.utils.arrayForEach(clsMapping.ClassServices(), function (svcMapping) {
                    svcMapping.ServiceRate(newValue);
                });
            });
        }
    });
};

var TaxClassMapping = function (taxClass, availableServices) {
    var self = this;
    
    self.TaxClass = ko.observable(taxClass);
    self.ClassServices = ko.observableArray(
        ko.utils.arrayMap(availableServices, function (svc) {
            return new TaxServiceMapping(svc);
        })
    );
};

var TaxClass = function(data) {
    var self = this;

    self.ServiceClassID = ko.observable(data.ServiceClassID);
    self.ServiceClassName = ko.observable(data.ServiceClassName);
};

var TaxServiceMapping = function (svc) {
    var self = this;
    
    self.TaxService = ko.observable(svc);
    self.ServiceRate = ko.observable("");
};

var TaxService = function(data) {
    var self = this;

    self.ServiceID = ko.observable(data.ServiceID);
    self.ServiceName = ko.observable(data.ServiceName);
};

function TaxRateViewModel() {
    var self = this;

    // I am getting...