Skip to content

Instantly share code, notes, and snippets.

@xwero
Created September 11, 2011 12:16
learn.knockoutjs.com pimped templates tutorial
<h2>Your seat reservations (<span data-bind="text: seats().length"></span>)</h2>
<table>
<thead><tr>
<th>Passenger name</th><th>Meal</th><th>Surcharge</th><th></th>
</tr></thead>
<tbody data-bind="template: {name:'reservationTemplate', foreach: seats}"></tbody>
</table>
<script type="text/x-jquery-tmpl" id="reservationTemplate">
<tr>
<td><input data-bind="value: name" /></td>
<td><select data-bind="options: availableMeals, value: meal, optionsText: 'mealName'"></select></td>
<td data-bind="text: formattedPrice"></td>
<td><a href="#" data-bind="click: remove">Remove</a></td>
</tr>
</script>
<h3 data-bind="visible: totalSurcharge() > 0">
Total surcharge: $<span data-bind="text: totalSurcharge().toFixed(2)"></span>
</h3>
<button data-bind="click: addSeat, enable: seats().length < 5">Reserve another seat</button>
// Raw catalog data - would come from the server
var availableMeals = [
{ mealName: "Standard (sandwich)", price: 0 },
{ mealName: "Premium (lobster)", price: 34.95 },
{ mealName: "Ultimate (whole zebra)", price: 290 }
];
// Class to represent a row in the reservations grid
var seatReservation = function(name) {
this.name = name;
this.availableMeals = availableMeals;
this.meal = ko.observable(availableMeals[0]);
this.formattedPrice = ko.dependentObservable(function() {
var price = this.meal().price;
return price ? "$" + price.toFixed(2) : "None";
}, this);
this.remove = function() { viewModel.seats.remove(this) }
}
// Overall viewmodel for this screen, along with initial state
var viewModel = {
seats: ko.observableArray([
new seatReservation("Steve"),
new seatReservation("Bert")
]),
addSeat: function() {
this.seats.push(new seatReservation());
}
};
viewModel.totalSurcharge = ko.dependentObservable(function() {
var total = 0;
for (var i = 0; i < this.seats().length; i++)
total += this.seats()[i].meal().price;
return total;
}, viewModel);
ko.applyBindings(viewModel);
<h2>Your seat reservations</h2>
<table>
<thead><tr>
<th>Passenger name</th><th>Meal</th><th>Surcharge</th><th></th>
</tr></thead>
<tbody id="seats"></tbody>
</table>
<p><button id="add-seat">Reserve another seat</button></p>
<div id="total-container">
<p>Total seats: <span id="total-seats"></span></p>
<p>Total surcharge: <span id="total-surcharge"></span></p>
</div>
<script type="text/x-jquery-tmpl" id="reservationTemplate">
{{each seats}}
<tr>
<td><input data-bind="value: name"></td>
<td><select data-bind="options: availableMeals, value: meal, optionsText: 'mealName'"></select></td>
<td data-bind="text: formattedPrice"></td>
<td><a href="#" data-bind="click: remove">Remove</a></td>
</tr>
{{/each}}
</script>
/**
* @preserve Unobtrusive Knockout support library for jQuery
*
* @author Joel Thoms
* @version 1.1
*/
(function($) {
if (!$ || !$['fn']) throw new Error('jQuery library is required.');
/**
* Private method to recursively render key value pairs into a string
*
* @param {Object} options Object to render into a string.
* @return {string} The string value of the object passed in.
*/
function render(options) {
var rendered = [];
for (var key in options) {
var val = options[key];
switch (typeof val) {
case 'string': rendered.push(key + ':' + val); break;
case 'object': rendered.push(key + ':{' + render(val) + '}'); break;
case 'function': rendered.push(key + ':' + val.toString()); break;
}
}
return rendered.join(',');
}
/**
* jQuery extension to handle unobtrusive Knockout data binding.
*
* @param {Object} options Object to render into a string.
* @return {Object} A jQuery object.
*/
$['fn']['dataBind'] = $['fn']['dataBind'] || function(options) {
return this['each'](function() {
var opts = $.extend({}, $['fn']['dataBind']['defaults'], options);
var attr = render(opts);
if (attr != null && attr != '') {
$(this)['attr']('data-bind', attr);
}
});
};
})(jQuery);
// Raw catalog data - would come from the server
var availableMeals = [
{ mealName: "Standard (sandwich)", price: 0 },
{ mealName: "Premium (lobster)", price: 34.95 },
{ mealName: "Ultimate (whole zebra)", price: 290 }
];
// Class to represent a row in the reservations grid
var seatReservation = function(name) {
this.name = name;
this.availableMeals = availableMeals;
this.meal = ko.observable(availableMeals[0]);
this.formattedPrice = ko.dependentObservable(function() {
var price = this.meal().price;
return price ? "$" + price.toFixed(2) : "None";
}, this);
var remove = function() {console.log(this.meal()); viewModel.seats.remove(this) };
this.remove = $.proxy(remove,this);
}
$('#seats').dataBind({ template: "'reservationTemplate'"});
$('#add-seat').dataBind({click: 'addSeat',enable: 'seats().length < 2'});
$('#total-container').dataBind({visible: 'seats().length > 0'});
$('#total-seats').dataBind({text: 'seats().length'});
$('#total-surcharge').dataBind({text: 'totalSurcharge() > 0 ? "$"+totalSurcharge().toFixed(2) : "None"'});
// Overall viewmodel for this screen, along with initial state
var viewModel = {
seats: ko.observableArray([
new seatReservation("Steve"),
new seatReservation("Bert")
]),
addSeat: function() {
var name = prompt("What is the passengers name?", "")
this.seats.push(new seatReservation(name));
},
totalSurcharge: function() {
var total = 0;
for (var i = 0; i < this.seats().length; i++)
total += this.seats()[i].meal().price;
return total;
}
};
ko.applyBindings(viewModel);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment