Grid editor example
An example of using the ‘foreach’ binding to render content for each item in an array. When you add or remove items, Knockout doesn’t need to re-render everything - it only renders the appropriate elements for the new item. The means the state of other rich UI controls (e.g., validators) isn’t wiped out.
For a detailed step-by-step tutorial about building this example and integrating it with ASP.NET MVC, see this blog post.
Live example
Source code: View
<form action="/?originalUrl=https%3A%2F%2Fknockoutjs.com%2F%26%2339%3B%2FsomeServerSideHandler%26%2339%3B%26gt%3B%2520%2520%2520%2520%26lt%3Bp%26gt%3BYou%2520have%2520asked%2520for%2520%26lt%3Bspan%2520data-bind%3D%26%2339%3Btext%3A%2520gifts().length%26%2339%3B%26gt%3B%26amp%3Bnbsp%3B%26lt%3B%2Fspan%26gt%3B%2520gift(s)%26lt%3B%2Fp%26gt%3B%2520%2520%2520%2520%26lt%3Btable%2520data-bind%3D%26%2339%3Bvisible%3A%2520gifts().length%2520%26gt%3B%25200%26%2339%3B%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3Bthead%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3Btr%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3Bth%26gt%3BGift%2520name%26lt%3B%2Fth%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3Bth%26gt%3BPrice%26lt%3B%2Fth%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3Bth%2520%2F%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3B%2Ftr%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3B%2Fthead%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3Btbody%2520data-bind%3D%26%2339%3Bforeach%3A%2520gifts%26%2339%3B%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3Btr%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3Btd%26gt%3B%26lt%3Binput%2520class%3D%26%2339%3Brequired%26%2339%3B%2520data-bind%3D%26%2339%3Bvalue%3A%2520name%2C%2520uniqueName%3A%2520true%26%2339%3B%2520%2F%26gt%3B%26lt%3B%2Ftd%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3Btd%26gt%3B%26lt%3Binput%2520class%3D%26%2339%3Brequired%2520number%26%2339%3B%2520data-bind%3D%26%2339%3Bvalue%3A%2520price%2C%2520uniqueName%3A%2520true%26%2339%3B%2520%2F%26gt%3B%26lt%3B%2Ftd%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3Btd%26gt%3B%26lt%3Ba%2520href%3D%26%2339%3B%23%26%2339%3B%2520data-bind%3D%26%2339%3Bclick%3A%2520%24root.removeGift%26%2339%3B%26gt%3BDelete%26lt%3B%2Fa%26gt%3B%26lt%3B%2Ftd%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3B%2Ftr%26gt%3B%2520%2520%2520%2520%2520%2520%2520%2520%26lt%3B%2Ftbody%26gt%3B%2520%2520%2520%2520%26lt%3B%2Ftable%26gt%3B%2520%2520%2520%2520%26lt%3Bbutton%2520data-bind%3D%26%2339%3Bclick%3A%2520addGift%26%2339%3B%26gt%3BAdd%2520Gift%26lt%3B%2Fbutton%26gt%3B%2520%2520%2520%2520%26lt%3Bbutton%2520data-bind%3D%26%2339%3Benable%3A%2520gifts().length%2520%26gt%3B%25200%26%2339%3B%2520type%3D%26%2339%3Bsubmit%26%2339%3B%26gt%3BSubmit%26lt%3B%2Fbutton%26gt%3B%26lt%3B%2Fform%26gt%3B%253C%2Fpre">Source code: View model
var GiftModel = function(gifts) { var self = this; self.gifts = ko.observableArray(gifts); self.addGift = function() { self.gifts.push({ name: "", price: "" }); }; self.removeGift = function(gift) { self.gifts.remove(gift); }; self.save = function(form) { alert("Could now transmit to server: " + ko.utils.stringifyJson(self.gifts)); // To actually transmit to server as a regular form post, write this: ko.utils.postJson($("form")[0], self.gifts); }; }; var viewModel = new GiftModel([ { name: "Tall Hat", price: "39.95"}, { name: "Long Cloak", price: "120.00"} ]); ko.applyBindings(viewModel); // Activate jQuery Validation $("form").validate({ submitHandler: viewModel.save });