Guard your model++
http://www.knockmeout.net/2011/03/guard-your-model-accept-or-cancel-edits.html
PAEz - added some stuff to check if an items new and delete it on a cancel.
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>
<table>
<tr>
<th>Name</th>
<th>Quantity</th>
<th></th>
<th></th>
</tr>
<tbody data-bind="template: { name: templateToUse, foreach: items}"></tbody>
</table>
<script id="itemTmpl" type="text/html">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: quantity"></td>
<td class="buttons">
<button data-bind="click: function() { viewModel.editItem($data); }, visible: !viewModel.isEditing()">Edit</button>
<button data-bind="click: function() { viewModel.deleteItem($data); }, visible: !viewModel.isEditing()">Delete</button>
</td>
</tr>
</script>
<button data-bind="click: newItem, enable: !viewModel.isEditing()">New Item</button>
<button onclick="Tester()">Tester</button>
<button onclick="viewModel.saveItems()">Save</button>
<button onclick="viewModel.loadItems()">Load</button>
<button onclick="viewModel.clearSavedItems()">Clear Save</button>
<script id="editTmpl" type="text/html">
<tr>
<td>
<input data-bind="value: name" />
</td>
<td>
<input data-bind="value: quantity" />
</td>
<td class="editButtons">
<button data-bind="click: function() { viewModel.acceptItemEdit() }, visible:viewModel.isEditing()">Accept</button>
<button data-bind="click: function() { viewModel.cancelItemEdit() }, visible:viewModel.isEditing()">Cancel</button>
<div data-bind="visible:isNew()"><br />Its new</div>
</td>
</tr>
</script>
CSS
input {
width: 75px;
}
table,
th,
td {
border-style: none;
margin: 0px;
padding: 0px;
}
td {
width: 80px;
height: 26px !important;
}
th {
color: #666;
font-size: .8em;
}
.buttons {
width: 150px;
}
.editButtons {
width: 150px;
}
.buttons button {
visibility: hidden;
opacity: 0;
-webkit-transition: opacity 0.2s ease-in-out;
}
tr:hover .buttons button {
visibility: visible;
opacity: 1;
}
.editButtons button {
opacity: 1;
-webkit-transition: opacity 0.2s ease-in-out;
}
.button {
visibility: visible;
opacity: 1;
-webkit-transition: opacity 0.2s ease-in-out;
}
JavaScript
//wrapper for an observable that protects value until committed
ko.protectedObservable = function(initialValue) {
//private variables
var _temp = initialValue;
var _actual = ko.observable(initialValue);
var result = ko.dependentObservable({
read: function() {
return _actual();
},
write: function(newValue) {
_temp = newValue;
}
});
//commit the temporary value to our observable, if it is different
result.commit = function() {
if (_temp !== _actual()) {
_actual(_temp);
}
};
//notify subscribers to update their value with the original
result.reset = function() {
_actual.valueHasMutated();
_temp = _actual();
};
return result;
};
//construct an Item
function Item(item) {
if ("new" in item) {
this.new = true; //ko.observable(item.isNew);
};
if ("name" in item) {
this.name = ko.protectedObservable(item.name);
} else {
this.name = ko.protectedObservable("");
};
if ("quantity" in item) {
this.quantity = ko.protectedObservable(item.quantity);
} else {
this.quantity = ko.protectedObservable(0);
};
this.commit = function() {
this.name.commit();
this.quantity.commit();
};
this.reset = function() {
this.name.reset();
this.quantity.reset();
};
//use this to set the modals header by
this.isNew = function() {
if ("new" in this) return true;
return false;
};
this.notNew = function() {
delete(this.new);
}
}
var viewModel = {
items: ko.observableArray(),
isEditing: ko.observable(),
selectedItem: ko.observable(),
addItem: function(item) {
var newItem = new Item(item);
this.items.push(newItem);
},
addItems: function(items) {
for (i = 0; i < items.length; i++) {
this.addItem(items[i]);
}
},
newItem: function() {
var newItem = new Item({
new: true
});
this.items.push(newItem);
this.selectedItem(newItem);
this.isEditing(true);
},
deleteItem:...