Knockout.js, jQuery UI, editable observableArray list
Short note about implementing most usefull knockout use case - editable list.
So we have list of items, and want to do some CRUD on it.
Here is sample code:
<!DOCTYPE html>
<html>
<head>
<title>list</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="components/jquery-ui/themes/base/jquery.ui.all.css" />
<link rel="stylesheet" href="components/bootstrap/docs/assets/css/bootstrap.css" />
</head>
<body>
<div class="container">
<table class="table">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>price</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: items">
<tr>
<td data-bind="text: id"></td>
<td data-bind="text: name"></td>
<td data-bind="text: price"></td>
<td>
<!-- call to editItem must be exactly the same, otherwise there will be event as second argument, look to addItem and editItem methods -->
<a href="#" class="btn btn-mini" data-bind="click: function(){$parent.editItem($data);}">Edit</a>
<a href="#" class="btn btn-mini" data-bind="click: function(){$parent.removeItem($data);}">Delete</a>
</td>
</tr>
</tbody>
</table>
<a href="#" class="btn btn-block" data-bind="click: addItem">Add Item</a>
<div id="editDisplay" class="form-horizontal" style="display:none">
<div class="control-group">
<label class="control-label">id</label>
<div class="controls">
<input class="input-block-level" type="number" data-bind="value: editor.id" disabled />
</div>
</div>
<div class="control-group">
<label class="control-label">name</label>
<div class="controls">
<input class="input-block-level" type="text" data-bind="value: editor.name" />
</div>
</div>
<div class="control-group">
<label class="control-label">price</label>
<div class="controls">
<input class="input-block-level" type="number" data-bind="value: editor.price" />
</div>
</div>
</div>
</div>
<script type="text/javascript" src="components/jquery/jquery.js"></script>
<script type="text/javascript" src="components/jquery-ui/ui/jquery-ui.custom.js"></script>
<script type="text/javascript" src="components/knockout/build/output/knockout-latest.debug.js"></script>
<script type="text/javascript">
// Our KISS item modell
function Item(id, name, price) {
this.id = ko.observable(id)
this.name = ko.observable(name)
this.price = ko.observable(price)
}
function List() {
var self = this
self.items = ko.observableArray([new Item(1, 'one', 5.5), new Item(2, 'two', 7.25), new Item(3, 'three', 4.75)])
self.addItem = function () {
// Here we create new item with default parameters
// and call editItem for it
// notice second argument wich is boolean
// indicating that this is new item
// IMPORTANT: in template you MUST use folow:
// data-bind="click: function(){$parent.editItem($data);}"
// otherwise it will pass event as second argument
var item = new Item(0, 'default', 0)
self.editItem(item, true)
}
self.removeItem = function (item) {
self.items.remove(item)
}
// dummy item model for editor
self.editor = new Item()
self.editItem = function (item, isNew) {
// populate editor values
self.editor.id(item.id())
self.editor.name(item.name())
self.editor.price(item.price())
// create dialog
$('#editDisplay').dialog({
modal: true,
resizable: false,
width: 400,
buttons: {
Accept: function () {
//TODO: validate here
$(this).dialog('close')
// save values from editor to item
item.name(self.editor.name())
item.price(self.editor.price())
// and add it if it is new one
if (isNew) {
self.items.push(item)
}
},
Cancel: function () {
$(this).dialog('close')
}
}
})
}
}
ko.applyBindings(new List())
</script>
</body>
</html>
And here is live demo: http://jsfiddle.net/mac2000/N2zNk/embedded/result/