Agile Web Development with Rails, Edition 4

11.2 Iteration F2: Creating an AJAX-Based Cart 10.4 Playtime

11.1 Iteration F1: Moving the Cart

<Your Cart> expected but was
<11.1 Iteration F1: Moving the Cart>.
<0> expected to be
>=
<1>.

Traceback:
  /home/rubys/git/awdwr/edition4/checkdepot.rb:209:in `block in <class:DepotTest>'

Refactor the cart view into partials, and reference the result from the layout.

Create a "partial" view, for just one line item

edit app/views/line_items/_line_item.html.erb
<tr>
  <td><%= line_item.quantity %>&times;</td>
  <td><%= line_item.product.title %></td>
  <td class="item_price"><%= number_to_currency(line_item.total_price) %></td>
</tr>

Replace that portion of the view with a callout to the partial

edit app/views/carts/show.html.erb
<% if notice %>
<p id="notice"><%= notice %></p>
<% end %>
 
<h2>Your Cart</h2>
<table>
  <%= render(@cart.line_items) %>
 
  <tr class="total_line">
    <td colspan="2">Total</td>
    <td class="total_cell"><%= number_to_currency(@cart.total_price) %></td>
  </tr>
 
</table>
 
<%= button_to 'Empty cart', @cart, method: :delete,
    data: { confirm: 'Are you sure?' } %>

Make a copy as a partial for the cart controller

cp app/views/carts/show.html.erb app/views/carts/_cart.html.erb

Modify the copy to reference the (sub)partial and take input from @cart

edit app/views/carts/_cart.html.erb
<h2>Your Cart</h2>
<table>
  <%= render(cart.line_items) %>
 
  <tr class="total_line">
    <td colspan="2">Total</td>
    <td class="total_cell"><%= number_to_currency(cart.total_price) %></td>
  </tr>
 
</table>
 
<%= button_to 'Empty cart', cart, method: :delete,
    data: { confirm: 'Are you sure?' } %>

Keep things DRY

edit app/views/carts/show.html.erb
<% if notice %>
<p id="notice"><%= notice %></p>
<% end %>
 
<%= render @cart %>

Reference the partial from the layout.

edit app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
  <title>Pragprog Books Online Store</title>
  <%= stylesheet_link_tag    "application", media: "all",
    "data-turbolinks-track" => true %>
  <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
  <%= csrf_meta_tags %>
</head>
<body class="<%= controller.controller_name %>">
  <div id="banner">
    <%= image_tag("logo.png") %>
    <%= @page_title || "Pragmatic Bookshelf" %>
  </div>
  <div id="columns">
    <div id="side">
      <div id="cart">
        <%= render @cart %>
      </div>
 
      <ul>
        <li><a href="http://www....">Home</a></li>
        <li><a href="http://www..../faq">Questions</a></li>
        <li><a href="http://www..../news">News</a></li>
        <li><a href="http://www..../contact">Contact</a></li>
      </ul>
    </div>
    <div id="main">
      <%= yield %>
    </div>
  </div>
</body>
</html>

Insert a call in the controller to find the cart

edit app/controllers/store_controller.rb
class StoreController < ApplicationController
  include CurrentCart
  before_action :set_cart
  def index
    @products = Product.order(:title)
  end
end

Add a small bit of style.

edit app/assets/stylesheets/carts.css.scss
 
 
.carts, #side #cart {
  .item_price, .total_line {
    text-align: right;
  }
 
  .total_line .total_cell {
    font-weight: bold;
    border-top: 1px solid #595;
  }
}
edit app/assets/stylesheets/application.css.scss
  #side {
    float: left;
    padding: 1em 2em;
    width: 13em;
    background: #141;
 
    form, div {
      display: inline;
    }  
 
    input {
      font-size: small;
    }
 
    #cart {
      font-size: smaller;
      color:     white;
 
      table {
        border-top:    1px dotted #595;
        border-bottom: 1px dotted #595;
        margin-bottom: 10px;
      }
    }
 
    ul {
      padding: 0;
 
      li {
        list-style: none;
 
        a {
          color: #bfb;
          font-size: small;
        }
      }
    }
  }

Change the redirect to be back to the store.

edit app/controllers/line_items_controller.rb
  def create
    product = Product.find(params[:product_id])
    @line_item = @cart.add_product(product.id)
 
    respond_to do |format|
      if @line_item.save
        format.html { redirect_to store_url }
        format.json { render :show,
          status: :created, location: @line_item }
      else
        format.html { render :new }
        format.json { render json: @line_item.errors,
          status: :unprocessable_entity }
      end
    end
  end

Purchase another product.

get /

Getting started

Here’s how to get rolling:

  1. Use bin/rails generate to create your models and controllers

    To see all available options, run it without parameters.

  2. Set up a root route to replace this page

    You're seeing this page because you're running in development mode and you haven't set a root route yet.

    Routes are set up in config/routes.rb.

  3. Configure your database

    If you're not using SQLite (the default), edit config/database.yml with your username and password.

11.2 Iteration F2: Creating an AJAX-Based Cart 10.4 Playtime