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);
});