I ran across this post while looking to solve a similar problem and figured I would post my approach and solution for the next guy.
I went with your line of thinking - clone the object and repopulate with old data on "undo":
1) Copy the data object into a new page variable ("_initData")
2) Create Observable from original server object
3) on "undo" reload observable with unaltered data ("_initData")
Simplified JS:
var _viewModel;
var _initData = {};
$(function () {
//on initial load
$.post("/loadMeUp", {}, function (data) {
$.extend(_initData , data);
_viewModel = ko.mapping.fromJS(data);
});
//to rollback changes
$("#undo").live("click", function (){
var data = {};
$.extend(data, _initData );
ko.mapping.fromJS(data, {}, _viewModel);
});
//when updating whole object from server
$("#updateFromServer).live("click", function(){
$.post("/loadMeUp", {}, function (data) {
$.extend(_initData , data);
ko.mapping.fromJS(data, {}, _viewModel);
});
});
//to just load a single item within the observable (for instance, nested objects)
$("#updateSpecificItemFromServer).live("click", function(){
$.post("/loadMeUpSpecificItem", {}, function (data) {
$.extend(_initData.SpecificItem, data);
ko.mapping.fromJS(data, {}, _viewModel.SpecificItem);
});
});
//updating subItems from both lists
$(".removeSpecificItem").live("click", function(){
//object id = "element_" + id
var id = this.id.split("_")[1];
$.post("/deleteSpecificItem", { itemID: id }, function(data){
//Table of items with the row elements id = "tr_" + id
$("#tr_" + id).remove();
$.each(_viewModel.SpecificItem.Members, function(index, value){
if(value.ID == id)
_viewModel.SpecificItem.Members.splice(index, 1);
});
$.each(_initData.SpecificItem.Members, function(index, value){
if(value.ID == id)
_initData.SpecificItem.Members.splice(index, 1);
});
});
});
});
I had an object that was complicated enough that I didn't want to add handlers for each individual property.
Some changes are made to my object in real time, those changes edit both the observable and the "_initData".
When I get data back from the server I update my "_initData" object to attempt to keep it in sync with the server.