Kncokout Create column and rows

by jiggle

HTML

<table>
    <tbody data-bind="foreach: rows">
        <tr>
            <td data-bind="text: id"></td>
            <!-- ko foreach: data -->
            <td>
                <input data-bind="value: value" />
            </td>
            <!-- /ko -->
        </tr>
    </tbody>
</table>


<hr>
<fieldset>
    <legend><b>Person</b></legend>
    <p>New name: <input data-bind="value: newName" /></p>
    <p>New age: <input data-bind="value: newAge" /></p>
    <button data-bind="click: addRow">Add Row</button>
</fieldset>
<hr>
<fieldset>
    <legend><b>Attributes</b></legend>
    <p>Name: <input data-bind="value: newAttribute, valueUpdate: 'afterkeydown'" /></p>
    <button data-bind="click: addColumn, enable: newAttribute().length">Add Column</button>
</fieldset>

<hr>

<button data-bind="click: save, enable: rows().length > 0">Save to JSON</button>
Last Saved JSON:
<textarea data-bind="value: lastSavedJson" rows="10" cols="60" disabled="disabled"> </textarea>

CSS

td, th { padding: 5px; }
input { width: 75px; }

JavaScript

function Cell(name, value) {
  this.name = ko.observable(name);
  this.value = ko.observable(value);
}

Cell.prototype.toJSON = function() {
    return ko.utils.unwrapObservable(this.value);   
}

function Person(id, name, age, additionalColumns) {
   var self = this;
   self.id = id;
   self.data = ko.observableArray([new Cell("name", name), new Cell("age", age)]);
   ko.utils.arrayForEach(additionalColumns || [], function(column) {
      self.data.push(new Cell(column));   
   });
}

var lastId=4;

var viewModel = {
    rows: ko.observableArray([
      new Person(1, "Bob", 44),
      new Person(2, "Ted", 22),
      new Person(3, "Jane", 55),
      new Person(4, "Sue", 11)
      ]),
    addColumn: function() {
        var newProperty = this.newAttribute();  

        ko.utils.arrayForEach(this.rows(), function(row) {
            row.data.push(new Cell(newProperty));
        });  
        
        this.additionalColumns.push(newProperty);
        
    },
    additionalColumns: ko.observableArray(),
    addRow: function() {
       this.rows.push(new Person(++lastId, this.newName(), this.newAge(), this.additionalColumns()));   
    },
    lastSavedJson: new ko.observable(""),
    save: function () {
        // the problem with this approach is sending unnecessary info
        this.lastSavedJson(ko.toJSON(viewModel.rows));
        
        // this won't work as expected
        //this.lastSavedJson(ko.utils.stringifyJson(this.rows()));
        
        // and this seems to do the same as above, but better formatted
        //this.lastSavedJson(JSON.stringify(viewModel.rows(), null, 2));
    },
    newName: ko.observable("New Person"),
    newAge: ko.observable("20"),
    newAttribute: ko.observable("Height")
};

ko.applyBindings(viewModel);