I'm having an issue with knockout js and mapping plugin with a hierarchical view model
My viewmodel is structured somewhat like this:
VM = {
members:[
{
name:"name 1",
volunteering:[{...},{...},{...}]
},
{
name:"name 1",
volunteering:[{...},{...},{...}]
}
]
}
Each member is in a tab, and each tab has a grid of volunteering activities. Clicking on an item in the grid pops up a dialog box to edit the volunteering activity.
At this point I clone the object to facilitate 'cancel edit' functionality
var Volunteer = {};
var koContext=ko.contextFor(this);
Volunteer = ko.mapping.toJS(koContext.$data); //plain js volunteer
Volunteer.index=koContext.$parent.EventVolunteers().indexOf(koContext.$data); //index of volunteer in member volunteer array
ko.applyBindings(ko.mapping.fromJS(Volunteer),$("#dialog-EditVolunteer")[0]); //bind new volunteer obj to dialog
Up to this point seems ok, clicking save on the dialog causes the issue.
var volunteer = ko.mapping.toJS(ko.contextFor(this).$data);
ko.mapping.fromJS(volunteer,{},ko.contextFor(currentTab).$data.EventVolunteers()[volunteer.index]);
At this point the properties get updated in the viewmodel, but not in the grid on the main screen.
It appears ko.mapping.fromJS is replacing the observable rather than updating it.
Personally I'm a fan of creating models like so. The abstraction here just makes more sense.
function VolunteerInfo(data){
var self = this;
self.activitiesName = ko.observable(data.name);
// any other info not all of it has to be mapped
// unless you plan on sending it back.
}
function MembersInfo(data){
var self = this;
self.name = ko.observable(data.name)// w.e it is labeled as under json
self.volunteering = ko.observableArray([]);
var mappedVolunteers = $.map(data.volunterring, function(item){
var volunteer = new VolunteerInfo(item);
return volunteer;
});
self.volunterring(mappedVolunteers);
}
function VM(){
var self = this;
self.members = ko.o
var request = $.getJSON("webservice address");
request.done(function () {
var mappedMembers = $.map(data.volunterring, function(item){
var member = new MemberInfo(item);
return member;
});
self.members(mappedMembers);
}
}
// apply bindings to VM ect ect.
I'm not certain I wholly understand exactly what you're doing.
But I was attempting to do undo functionality in a list of items, and I used this on the array:
observableArray.replace(newData, ko.mapping.fromJS(original))
I get the new data as an argument of the click handler.
When I store the original, I basically have this:
//Dupe check
self.undoCache.Emails.pop(jQuery.grep(self.undoCache.Emails, function (element, index) { return element.Id == data.Id(); })[0]);
//Store original
self.undoCache.Emails.push(ko.mapping.toJS(data));
The "Emails" are the objects I'm editing. I only store the original data, not the observable. That allowed me to use replace. I'm not certain how correct that is, per se, but canceling works for me.
My eventual solution in this case was to set the properties on the original VM, with the edited values from the 'cloned' viewmodel.
For new projects however, I now use a knockout plugin