protectedObjectObservable
https://groups.google.com/d/topic/knockoutjs/_HTI5QvG7Ls/discussion
by photo_tom
HTML
<script src="http://rniemeyer.github.com/KnockMeOut/Scripts/jquery.tmpl.js"></script>
<script src="http://rniemeyer.github.com/KnockMeOut/Scripts/knockout-latest.debug.js"></script>
<script src="https://raw.github.com/SteveSanderson/knockout.mapping/master/build/output/knockout.mapping-latest.js"></script>
<ul data-bind="template: { name: 'itemTmpl', foreach: items }"></ul>
<script id="itemTmpl" type="text/html">
<li>
<a href="#" data-bind="text: name, click: function() { viewModel.selectedItem.init($data); }"></a>
</li>
</script>
<hr />
<div data-bind="template: { name: 'editorTmpl', data: selectedItem.temp }"></div>
<script id="editorTmpl" type="text/html">
<input data-bind="value: name" />
<input data-bind="value: description" /><br/>
<button data-bind="click: viewModel.save">Save</button>
<button data-bind="click: viewModel.cancel">Cancel</button>
</script>
<div data-bind="text: ko.toJSON(viewModel.items)"></div>
JavaScript
ko.protectedObjectObservable = function(initialValue) {
var result = ko.observable(initialValue);
//expose temp value for binding. ko.toJS is an easy way to get a clean copy
result.temp = ko.observable(ko.toJS(initialValue));
//apply edits to the original (only goes one level deep)
result.commit = function() {
var original = result(),
edited = result.temp();
for (var prop in edited) {
if (original.hasOwnProperty(prop)) {
if (ko.isWriteableObservable(original[prop])) {
original[prop](edited[prop]);
//ignore dependentObservables + methods
} else if (typeof original[prop] !== "function") {
original[prop] = edited[prop];
}
}
}
return result; //for chaining
};
//start over with a new value
result.init = function(newValue) {
result.temp(ko.toJS(newValue));
result(newValue);
return result; //for chaining
}
//revert back to a fresh copy
result.reset = function(newValue){
result.temp(ko.toJS(result));
return result; //for chaining
};
//clear it out, so it is ready for a new one
result.clear = function() {
result.temp(null);
result(null);
return result; //for chaining
}
return result;
};
function Item(id, name, description) {
return {
id: id,
name: ko.observable(name),
description: ko.observable(description)
};
}
var viewModel = {
items: ko.observableArray([
new Item(1, "one", "one description"),
new Item(2, "two", "two description"),
new Item(3, "three","three description")
]),
selectedItem: ko.protectedObjectObservable(),
save: function() {
viewModel.selectedItem.commit().clear();
},
cancel: function() {
...