Reputation: 83858
I am trying to figure out the canonical way to add elements to a list in a model created with ko.mapping. I created a jsFiddle to tinker:
The javascript is something along the lines of the following:
var mapping, baseModel, view_model;
mapping = {
outer: [{
alpha: '',
beta: [
{
carotine: '',
blockers: ''}
]
}]
};
baseModel = { // add in functionality
append_outer: function() {
this.outer.push({});
},
append_beta: function(xyz) {
this.beta.push({});
}
};
view_model = ko.mapping.fromJS(mapping, {}, baseModel);
ko.applyBindings(view_model, $("#mapped")[0]);
The corresponding HTML is on the jsFiddle.
When I use append_outer
I would expect to have a new element added to the view_model essentially identical to the first element, all with observables. When I use append_beta
I would expect beta to have a new element with [carotine: ko.observable(), blockers: ko.observable]
. I have not seen an obvious way to do this - but I would expect it to be a very common use-case.
For obvious reasons the sample code is not doing what I desire! It is of course just what I was tinkering in the hopes of finding something obvious, e.g. ko.mappingCreate
or some such. Alas, no such luck.
It is perhaps mentioning that the data model is dynamic - though lists will always have items with data representations identical to that of their peers. One might say that the data used to create the view model (mapping
) is a prototype.
I would be grateful for any thoughts and direction.
Upvotes: 0
Views: 1606
Reputation: 83858
Working off Keith's suggestion, I did the following:
var mapping, baseModel, view_model;
mapping = {
outer: [{
alpha: '',
beta: [
{
carotine: '',
blockers: '',
skittles: ''}
]}
]
};
baseModel = function(mapping) { // add in functionality
var self = this;
this._vm_prototype = mapping;
console.log("Prototype: " + ko.mapping.toJSON(this._vm_prototype));
self.append = function(target, proto_getter) {
console.log("pg: " + proto_getter);
var prototype = proto_getter(self._vm_prototype);
console.log("Prototype: " + JSON.stringify(prototype));
console.log("Target: " + ko.mapping.toJSON(target));
this[target].push(ko.mapping.fromJS(prototype));
};
};
view_model = ko.mapping.fromJS(mapping, {}, new baseModel(mapping));
ko.applyBindings(view_model, $("#mapped")[0]);
Unfortunately this requires something of a hack on the HTML side, being e.g. to add a new beta
one has to identify where in the prototype it exists, i.e.
<button data-bind='click: $root.append.bind($data, "beta",
function (proto) { return proto.outer[0].beta[0]; })'>
Add another beta
</button>
and for an outer
<button data-bind='click: append.bind($data, "outer",
function (proto) { return proto.outer[0]; })'>
Add another outer
</button>
The append
function could be made cleaner if it were just passed outer[0].beta[0]
in place of the function, but then one would have to parse or eval
this.
As one can see from the jsFiddle A and B, the only thing that changes is the mapping
, but the functionality works as expected for both.
Again, I am trying to avoid duplication of code, but this is ugly. I would love any thoughts on how to clean this up!
Upvotes: 0
Reputation: 44316
Not that I'd do it this way... but make a object for the types that takes aspects of the mapping as a template.
http://jsfiddle.net/keith_nicholas/EEE6J/
In general, I'd just define the structure with the objects and just use the json for data.
Upvotes: 1