JSFiddle - React, Tailwind, and code Playground

by robgallen

HTML

<script src="https://rawgithub.com/SteveSanderson/knockout.mapping/master/build/output/knockout.mapping-latest.js"></script>
<em>To show editor pattern on grandchildren</em>
<table>
    <thead>
        <tr>
            <th></th>
            <th>Period</th>
            <th>Price</th>
            <th>Amount</th>
            <th></th>
        </tr>
    </thead>
    <tbody data-bind="foreach: data.Items">
        <tr>
            <td data-bind="text: Title" class="hdr" colspan="5"></td>
        </tr>
        <!-- ko foreach: Data -->
        <tr>
            <td>
                <input data-bind="checked: isSelected" type="checkbox" />
            </td>
            <td data-bind="text: Period"></td>
            <td>
                <input data-bind="value: Price" type="text" />
            </td>
            <td>
                <input data-bind="value: Amount" type="text" />
            </td>
            <td>
                <button data-bind="click: update">Update</button>
                <button data-bind="click: revert">Cancel</button>
            </td>
        </tr>
        <!-- /ko -->
    </tbody>
</table>

<hr />

<em>To show isSelected pattern on grandchildren</em>
<ul data-bind="foreach: selectedItems">
    <li>
        <span data-bind="text: Period"></span> : 
        <span data-bind="text: Price"></span> x 
        <span data-bind="text: Amount"></span>
    </li>
</ul>

CSS

table {
    border-collapse: collapse;
    font-family: sans-serif;
}
th, td {
    border: 1px solid #ddd;
}
input {
    border: 1px solid #bbb;
    padding: 2px 5px;
    text-align: right;
    width: 70px;
}
.hdr {
    font-weight: bold;
    padding: 5px 10px;
}

JavaScript

// dummy data from api call
var data = {
    Items: [{
        Title: "This is item one",
        Data: [
            { Id: 1, Period: "Dec 2013", Price: "10", Amount: 5 },
            { Id: 2, Period: "Jan 2014", Price: "12", Amount: 6 },
            { Id: 3, Period: "Feb 2014", Price: "15", Amount: 10 }
        ]
    }, {
        Title: "This is item two", 
        Data: [
            { Id: 1, Period: "Dec 2013", Price: "16", Amount: 50 }, 
            { Id: 2, Period: "Jan 2014", Price: "19", Amount: 45 }, 
            { Id: 3, Period: "Feb 2014", Price: "21", Amount: 70 }
        ] 
    }]
};

var mapping = {
    create: function (options) {
        var dataMap = {
            "Data": {
                create: function (opts) {
                    var item = ko.mapping.fromJS(opts.data);
                    item.isSelected = ko.observable(false);

                    // add editor pattern
                    item.cache = function () {};
                    item.update = function (data) {
                        item.cache.latestData = ko.toJS(data);
                        ko.mapping.fromJS(item.cache.latestData, this);
                    };
                    item.revert = function () {
                        item.update(item.cache.latestData);
                    };
                    item.update(opts.data);
                    return item;
                }
            }
        };
        var model = ko.mapping.fromJS(options.data, dataMap);
        return model;
    }
};

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

    self.data = ko.mapping.fromJS(data, mapping);

    self.selectedItems = ko.computed(function () {
        // data.Items[n].Data[n].isSelected
        var list = [];
        ko.utils.arrayForEach(self.data.Items(), function (item) {
            ko.utils.arrayForEach(item.Data(), function (data) {
                if (data.isSelected()) {
                    list.push(data);
                }
            });
        });
       ...