learn.knockoutjs.com - Working with templates and lists

http://learn.knockoutjs.com/#/?tutorial=templates

HTML

<script src="http://learn.knockoutjs.com/Scripts/Lib/jquery.tmpl.js"></script>
<script src="http://learn.knockoutjs.com/Scripts/Lib/jquery.address.js"></script>
<script src="http://learn.knockoutjs.com/Scripts/Lib/knockout-1.3.0.latest.js"></script>
<link rel="stylesheet" href="http://learn.knockoutjs.com/Content/App/coderunner.css">
<link rel="stylesheet" href="http://learn.knockoutjs.com/Content/TutorialSpecific/templates.css">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0rc2/jquery.mobile-1.0rc2.min.css">
<script src="http://code.jquery.com/mobile/1.0rc2/jquery.mobile-1.0rc2.min.js"></script>
<body>
    <div data-role="page">
        <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: {name:'reservationTemplate', foreach: seats, afterAdd: function() { $('.reservationItem').trigger('create') }}">
            </div>
            
            <script type="text/x-jquery-tmpl" id="reservationTemplate"> 
                <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: availableMeals, value: meal, optionsText:...

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

(function() { // Wrap in function to prevent accidental globals
    // 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);
})();