Reputation: 1511
I am trying to utilize knockout to create an editable table. I have a JSON object that has a collection of both headings and table data. This table needs to be built using any object. It will loop over the JSON object to create ko.observableArray and the ko.observables to populate it. I have been able to do that. My problem is that the ko.observables are not being data bound.
Here is a snippet of my JavaScript:
EditableTableVM.prototype.load = function() {
this.headings = this.buildKO_ObservableArray(new Heading(), this.dataRepo.HEADINGS);
this.listings = this.buildKO_ObservableArray(new Listing(), this.dataRepo.LISTINGS);
}
/*
* Dynamically creates a ko.observableArray from a JS array
* Params: JSClass - new instance of a class
* Params: baseArray - array of objects to create the observable array
* Returns: Observable arary of JS object with ko.observables
*/
EditableTableVM.prototype.buildKO_ObservableArray = function(JSClass, baseArray) {
var newArray = ko.observableArray([]);
for(var i = 0, j = baseArray.length; i < j; i++) {
var baseObj = baseArray[i];
//new class is the class with ko.observables properties
var newClass = this.buildKO_Observable(JSClass, baseObj);
newArray.push(newClass);
}
return newArray;
}
/*
* Dynamically create ko.observable from properties in an object
* Params: JSClass - new instance of a class
* Params: jsObject - object to created observables with
* Returns: JS object with ko.observables
*/
EditableTableVM.prototype.buildKO_Observable = function(JSClass, jsObj) {
for (var key in jsObj) {
if (jsObj.hasOwnProperty(key)) {
JSClass[key] = ko.observable(jsObj[key]);
}
}
return JSClass;
}
Here is my Fiddle http://jsfiddle.net/breck421/YFNLX/ with it working up to the point I described earlier.
I am not sure if what I am trying to do is possible or not and would really appreciate another set of eyes on this.
Thanks,
Jordan
Upvotes: 2
Views: 5105
Reputation: 1802
The issue you're running into is that when you're mapping your JSON to knockout-enabled view models, you aren't handling children with arrays correctly. Your mapping isn't going deep enough.
If you check the following:
EditableTableVM.listings()[0].LISTING()[0];
you'll see that the properties of that object are just values, not ko.observables.
Rather than try to build this mapping from scratch, I'd recommend using one of the knockout mapping plugins : either the knockout mapping plugin here : https://github.com/SteveSanderson/knockout.mapping with it's documentation here : http://knockoutjs.com/documentation/plugins-mapping.html or the newer Knockout ViewModel plugin here : http://coderenaissance.github.io/knockout.viewmodel/
I've updated your fiddle with the knockout mapping plugin : http://jsfiddle.net/rrahlf/YFNLX/2/ and now your values bind correctly.
Good luck!
Upvotes: 1
Reputation: 16688
It looks like you want to use something like http://coderenaissance.github.io/knockout.viewmodel/
EditableTableVM.prototype.load = function () {
this.headings = ko.viewmodel.fromModel(this.dataRepo.HEADINGS);
this.listings = ko.viewmodel.fromModel(this.dataRepo.LISTINGS);
}
http://jsfiddle.net/mbest/YFNLX/3/
Upvotes: 1
Reputation: 19847
Something like this (fiddle)
Javascript:
var DynamicObservable = function(data) {
for (var key in data) {
this[key] = ko.observable(data[key]);
}
};
var ViewModel = function(data) {
var self = this;
self.items = ko.observableArray(ko.utils.arrayMap(data, function(i) {
return new DynamicObservable(i);
}));
self.columns = ko.observableArray();
for (var key in data[0]) {
self.columns.push(key);
}
};
HTML
<table>
<thead>
<tr data-bind="foreach: columns">
<th data-bind="text: $data"></th>
</tr>
</thead>
<tbody data-bind="foreach: { data: items, as: 'item'}">
<tr data-bind="foreach: $parent.columns">
<td><input data-bind="value: item[$data]" /></td>
</tr>
</tbody>
</table>
You could definitely improve on this if you wanted to be able to create new objects. Just store the "columns" and use it to initialize a new type, but with empty values.
Note, the check for hasOwnProperty
is useless. Since you data is coming from JSON it will always be true.
Upvotes: 1