Creating a smart, dirty flag
http://www.knockmeout.net/2011/05/creating-smart-dirty-flag-in-knockoutjs.html
by craigcav
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>
<ul data-bind="template: { name: 'itemsTmpl', foreach: items }"></ul>
<hr/>
<script id="itemsTmpl" type="text/html">
<li data-bind="css: { dirty: dirtyFlag.isDirty }">
<span data-bind="text: id"></span>
<input data-bind="value: name" />
<button data-bind="enable: dirtyFlag.isDirty, click: dirtyFlag.reset">Reset</button>
<button data-bind="enable: dirtyFlag.hasRollback, click: dirtyFlag.rollback">Rollback</button>
</li>
</script>
<button data-bind="enable: isDirty, click: save">Save</button>
<hr />
<h3>Just Dirty Items</h3>
<div data-bind="text: ko.toJSON(dirtyItems)"></div>
CSS
li { padding: 2px; margin: 2px; }
input { width: 75px; }
.dirty { border: solid yellow 2px; }
JavaScript
ko.dirtyFlag = function(root, isInitiallyDirty) {
var result = function() {},
_state = ko.observable(ko.toJSON(root)),
_isDirty = ko.observable(isInitiallyDirty);
result.isDirty = ko.dependentObservable(function() {
return _isDirty() || _state() !== ko.toJSON(root);
});
result.reset = function() {
_state(ko.toJSON(root));
_isDirty(false);
if (isInitiallyDirty) {
isInitiallyDirty = false;
}
};
result.hasRollback = ko.dependentObservable(function() {
return result.isDirty() && !isInitiallyDirty;
});
result.rollback = function() {
if (!result.hasRollback()) {
return;
}
var data = ko.utils.parseJson(_state()), key;
for (key in data) {
if (data.hasOwnProperty(key)) {
if (ko.isObservable(root[key])) {
root[key](data[key]);
}
else {
root[key] = data[key];
}
}
}
result.reset();
};
return result;
};
function Item(id, name, isDirty) {
this.id = ko.observable(id);
this.name = ko.observable(name);
// non observed properties and methods
this.xx = Math.round(Math.random()*10);
this.yy = function(){alert(1);}
// NB! to handle changes properly run after initialization
this.dirtyFlag = ko.dirtyFlag(this, isDirty);
}
var viewModel = {
items: ko.observableArray([
new Item(1, "one", true),
new Item(2, "two"),
new Item(3, "three", true)
]),
save: function() {
alert("Sending changes to server: " + ko.toJSON(this.dirtyItems));
}
};
viewModel.dirtyItems = ko.dependentObservable(function() {
return ko.utils.arrayFilter(this.items(), function(item) {
return item.dirtyFlag.isDirty();
});
}, viewModel);
viewModel.isDirty = ko.dependentObservable(function() {
...