koo-mappable array

by mckennatim

HTML

<script src="//cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.3.5/knockout.mapping.js"></script>
<table id="myTable" class="tablesorter">
    <thead>
        <tr>
            <th>Mon  1 </th>
            <th>Mon  2 </th>
            <th>Mon  3 </th>
            <th>Total </th>
        </tr>
    </thead>
    <tbody data-bind='foreach:List'>
        <tr>
            <!-- ko foreach:Entries-->
            <td>
                <input size="3" type="text" data-bind="value:Hours, valueUpdate:'afterkeydown'"/>
            </td>
            <!-- /ko -->
            <td>
                <span data-bind="text:Total"></span> 
            </td>
        </tr>
    </tbody>
</table>

JavaScript

function ViewModel() {
    var self = this
    self.List = ko.observableArray([])

    self.LoadData = function () {
        var data = {
            "Lines": [
                {"Entries": [{"Hours": 5.5},{"Hours": 2.50},{"Hours": 3.75}]}, 
                {"Entries": [{"Hours": 5.1},{"Hours": 2.00},{"Hours": 4.75}]},
                {"Entries": [{"Hours": 1.2},{"Hours": 3.00},{"Hours": 2.12}]
            }]
        }                       
        self.List(ko.mapping.fromJS(data.Lines)())
    ////this makes every child observable
    }
    
    self.applyTotals = function(){
        ko.utils.arrayForEach(self.List(), function(vm){
            vm.Total = ko.computed(function(){
                var s = 0;
                ko.utils.arrayForEach(this.Entries(), function(entry){
                    var p = parseFloat(entry.Hours(), 10);
                    if (!isNaN(p)) {
                        s += p;
                    }
                });
                return s;
            }, vm);
        });
    }
    
    self.LoadData();
    console.log(self.List());
    self.applyTotals();
}

ko.applyBindings(new ViewModel())