learn.knockoutjs.com - Working with templates and lists
http://learn.knockoutjs.com/#/?tutorial=templates
HTML
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css">
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js"></script>
<script src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-2.0.0.debug.js"></script>
<body>
<div data-role="page" id="tut2">
<div data-role="header">
<h1>
Tutorial 2
</h1>
</div>
<div data-role="content">
<div class="content-textbox">
Your seat reservations (<span data-bind="text: seats().length"></span>)
</div>
<div data-bind="template: { foreach: seats, afterRender: function() { $('.reservationItem').trigger('create'); }}">
<div class="reservationItem">
<ul data-role="listview" data-inset="true">
<li data-role="list-divider">
Reservation
</li>
<li>
<div data-role="fieldcontain">
<label for="name">Name</label>
<input id="name" data-bind="value: name" />
</div>
<div data-role="fieldcontain">
<label for="meal">Meal</label>
<select id="meal" data-bind="options: $root.availableMeals, value: meal, optionsText: 'mealName'"></select>
</div>
<div data-role="fieldcontain">
<label class="ui-input-text">Price</label>
<span data-bind="text: formattedPrice"></span>
</div>
<div data-role="fieldcontain">
<a href="#" data-role="button" data-bind="click: $root.removeSeat">Remove</a>
</div>
</li>
</ul>
</div>
</div>
<div class="content-textbox" data-bind="visible: totalSurcharge() > 0">
Total surcharge: $<span data-bind="text: totalSurcharge().toFixed(2)"></span>
</div>
<button data-bind="click:...
CSS
.content-textbox {
background: none repeat scroll 0 0 #F9F9F9;
box-shadow: 0 0 3px #CCCCCC;
line-height: 1.2em;
margin-bottom: 15px;
margin-top: 10px;
padding: 8px;
}
JavaScript
// Class to represent a row in the seat reservations grid
function SeatReservation(name, initialMeal) {
var self = this;
self.name = name;
self.meal = ko.observable(initialMeal);
self.formattedPrice = ko.computed(function() {
var price = self.meal().price;
return price ? "$" + price.toFixed(2) : "None";
});
}
// Overall viewmodel for this screen, along with initial state
function ReservationsViewModel() {
var self = this;
// Non-editable catalog data - would come from the server
self.availableMeals = [
{ mealName: "Standard (sandwich)", price: 0 },
{ mealName: "Premium (lobster)", price: 34.95 },
{ mealName: "Ultimate (whole zebra)", price: 290 }
];
// Editable data
self.seats = ko.observableArray([
new SeatReservation("Steve", self.availableMeals[0]),
new SeatReservation("Bert", self.availableMeals[0])
]);
// Computed data
self.totalSurcharge = ko.computed(function() {
var total = 0;
for (var i = 0; i < self.seats().length; i++)
total += self.seats()[i].meal().price;
return total;
});
// Operations
self.addSeat = function() {
self.seats.push(new SeatReservation("", self.availableMeals[0]));
}
self.removeSeat = function(seat) { self.seats.remove(seat) }
}
var reservationsViewModel = new ReservationsViewModel();
$('#tut2').live('pagecreate', function() {
console.log('pagecreate');
ko.applyBindings(reservationsViewModel);
});