JSFiddle - React, Tailwind, and code Playground

by graphettion

HTML

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<div class="container">
  <div class="content">
    <div class="row">
      <div class="span8">
        <h1>Example 3 :: Shopping Cart</h1>
        <hr />

        <h2>Products</h2>
        <hr />

        <!-- Iterate over view_mode.products (Observable Array) -->
        <ul data-bind="foreach:products" class="thumbnails">

          <!-- The HTML within this <ul></ul> block will get rendered for each item in view_model.products -->

          <li class="span2">
            <div class="thumbnail">
              <img src="http://placehold.it/160x100" alt="">
              <div class="caption">

                <!-- [Product] $data.name (Observable) -->
                <h5 data-bind="text:$data.name"></h5>

                <!-- [Product] $data.price (Computed Observable) -->
                <p>$<span data-bind="text:$data.price().formatMoney(2,'.',',')"></span></p>

                <!-- 
          - $parent is a special object used to reach above the scope of the foreach loop we're currently in.
          - Events binding `click:$parent.addToCart` executes an Action Method in the View Model
          - Knockout automaticlly passes $data as the first parameter, and the `events` object as the second parameter.
        -->
                <a data-bind="click: $parent.addToCart" href="#" class="btn btn-mini btn-success">
                  <i class="icon-plus icon-white"></i> Add
                </a>

              </div>
            </div>
          </li>

        </ul>

        <h2>Cart</h2>
        <hr />
        <div class="row-fluid">
          <div class="span8">
            <table class="table table-bordered">
              <thead>
                <tr>
                  <th...

CSS

ul li {
  list-style: none;
}

JavaScript

Number.prototype.formatMoney = function(c, d, t) {
  var n = this,
    c = isNaN(c = Math.abs(c)) ? 2 : c,
    d = d == undefined ? "," : d,
    t = t == undefined ? "." : t,
    s = n < 0 ? "-" : "",
    i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "",
    j = (j = i.length) > 3 ? j % 3 : 0;
  return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
};

// CLASS Product 
var Product = function(id, name, price) {
  this.id = ko.observable(id);
  this.name = ko.observable(name);
  this.price = ko.observable(price);
};

// CLASS CartItem 
var CartItem = function(product, quantity) {
  var self = this; // Scope Trick

  self.product = ko.observable(product);
  self.quantity = ko.observable(quantity || 1);

  self.cost = ko.computed(function() {
    return self.product().price() * self.quantity();
  });
};

// CLASS ViewModel 
var ViewModel = function() {
  var self = this; // Scope Trick

  /**
   * Observables
   */
  self.sales_tax = ko.observable(0.07);
  self.shipping_cost = ko.observable(10.00);

  /**
   * Observable Arrays
   */
  self.cart = ko.observableArray();
  self.products = ko.observableArray();

  /**
   * Computed Observables
   */
  self.subtotal = ko.computed(function() {
    var subtotal = 0;
    $(self.cart()).each(function(index, cart_item) {
      subtotal += cart_item.cost();
    });
    return subtotal;
  });

  self.tax = ko.computed(function() {
    return self.subtotal() * self.sales_tax();
  });

  self.total = ko.computed(function() {
    return self.shipping_cost() + self.subtotal() + self.tax();
  });

  /**
   * Actions
   */
  self.addToCart = function(product, event) {
    // Instantiate a new CartItem object using the passed
    // in `Product` object, and then set a quantity of 1.
    var cart_item = new CartItem(product, 1);

    // Add the CartItem instance to the self.cart (Observable Array)
    self.cart.push(cart_item);
  };

 ...