JSFiddle - React, Tailwind, and code Playground
HTML
<header>
<div class="content-wrapper">
<div class="float-left">
<p class="site-title"><a href="/">Fresh Water Buoys</a></p>
</div>
<div class="float-right">
<!--
<section id="login">
Hello, <span class="username">SOM\roland.wales</span>!
</section>
-->
<nav>
<ul id="menu">
<li><a href="/">Home</a></li>
<li><a href="/Home/About">About</a></li>
<li><a href="/Home/Contact">Contact</a></li>
</ul>
</nav>
</div>
</div>
</header>
<div id="body">
<section class="content-wrapper main-content clear-fix">
<h2>Your seat reservations (<span data-bind="text: seats().length"></span>)</h2>
<table>
<thead>
<tr>
<th>Passenger name</th>
<th>Meal</th>
<th>Modified name</th>
<th>Surcharge</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: seats">
<tr>
<td>
<input data-bind="value: name, click: $root.nameClick" />
</td>
<td>
<select data-bind="options: $root.availableMeals, value: meal, optionsText: 'mealName'"></select>
</td>
<td data-bind="text: modifiedName"></td>
<td data-bind="text: formattedPrice"></td>
<td><a href="#" data-bind="click: $root.removeSeat">Remove</a></td>
</tr>
</tbody>
</table>
<button data-bind="click: addSeat, enable: seats().length < 5">Reserve another seat</button>
<h3 data-bind="visible: totalSurcharge() > 0">
Total surcharge: $<span data-bind="text: totalSurcharge().toFixed(2)"></span>
</h3>
</section>
</div>
JavaScript
// Class to represent a row in the seat reservations grid
function SeatReservation(name, initialMeal) {
var self = this;
self.name = ko.observable(name);
self.meal = ko.observable(initialMeal);
self.modifiedName = ko.computed(function() {
var name2 = self.name();
self.name(name2);
return name2;
});
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.destroy(seat)
} /* use destroy instead of remove to set _destroy property */
/*
* This updates the name observable to "a new name".
*/
self.nameClick = function(seat) {
seat.name("a new name");
seat.name.valueHasMutated();
}
}
ko.applyBindings(new ReservationsViewModel());