I'm working through Agile Web Development with Rails 4, stuck on Chapter 11, Iteration F4. The goal of the section is to have a shopping cart div display only when there are items in the cart, otherwise hide it via display: none. The cart does properly hide itself when the Empty Cart button is clicked, but when an item is then added to it, the cart doesn't display unless the page is then manually refreshed.
Here's what the server output looks like when an item is added to an empty cart:
Started POST "/line_items?product_id=2" for ::1 at 2015-05-10 18:51:44 -0700
Processing by LineItemsController#create as JS
Parameters: {"authenticity_token"=>"v2zcRr2CPsfZP3/qI8l5m0HWdDoOiiyl5oiZxvpYKXp7K2ecXizzCZA37DLm7PwYuSAgemogwjjnDHz4NbavGA==", "product_id"=>"2"}
Cart Load (0.2ms) SELECT "carts".* FROM "carts" WHERE "carts"."id" = ? LIMIT 1 [["id", 21]]
Product Load (0.1ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT 1 [["id", 2]]
LineItem Load (0.1ms) SELECT "line_items".* FROM "line_items" WHERE "line_items"."cart_id" = ? AND "line_items"."product_id" = ? LIMIT 1 [["cart_id", 21], ["product_id", 2]]
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "line_items" ("product_id", "cart_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["product_id", 2], ["cart_id", 21], ["created_at", "2015-05-11 01:51:44.341300"], ["updated_at", "2015-05-11 01:51:44.341300"]]
(1.1ms) commit transaction
LineItem Load (0.2ms) SELECT "line_items".* FROM "line_items" WHERE "line_items"."cart_id" = ? [["cart_id", 21]]
Product Load (0.2ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT 1 [["id", 2]]
Rendered line_items/_line_item.html.erb (2.0ms)
Rendered carts/_cart.html.erb (6.7ms)
Rendered line_items/create.js.erb (8.9ms)
And here's what the output looks like when I refresh:
Started GET "/" for ::1 at 2015-05-10 18:57:17 -0700
Processing by StoreController#index as HTML
Cart Load (0.1ms) SELECT "carts".* FROM "carts" WHERE "carts"."id" = ? LIMIT 1 [["id", 21]]
Product Load (2.2ms) SELECT "products".* FROM "products" ORDER BY "products"."updated_at" DESC LIMIT 1
Product Load (0.2ms) SELECT "products".* FROM "products" ORDER BY "products"."title" ASC
Rendered store/index.html.erb within layouts/application (17.9ms)
LineItem Exists (0.2ms) SELECT 1 AS one FROM "line_items" WHERE "line_items"."cart_id" = ? LIMIT 1 [["cart_id", 21]]
LineItem Load (0.2ms) SELECT "line_items".* FROM "line_items" WHERE "line_items"."cart_id" = ? [["cart_id", 21]]
Product Load (0.1ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT 1 [["id", 2]]
Rendered line_items/_line_item.html.erb (1.6ms)
Rendered carts/_cart.html.erb (6.2ms)
Completed 200 OK in 129ms (Views: 124.7ms | ActiveRecord: 3.0ms)
Here's my LineItemsController, containing the create action triggered when I add the item to the empty cart:
class LineItemsController < ApplicationController
include CurrentCart
before_action :set_cart, only: [:create]
before_action :set_line_item, only: [:show, :edit, :update, :destroy]
# GET /line_items
# GET /line_items.json
def index
#line_items = LineItem.all
end
# GET /line_items/1
# GET /line_items/1.json
def show
end
# GET /line_items/new
def new
#line_item = LineItem.new
end
# GET /line_items/1/edit
def edit
end
# POST /line_items
# POST /line_items.json
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.js { #current_item = #line_item }
format.json { render action: 'show', status: :created, location: #line_item }
else
format.html { render action: 'new' }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /line_items/1
# PATCH/PUT /line_items/1.json
def update
respond_to do |format|
if #line_item.update(line_item_params)
format.html { redirect_to #line_item, notice: 'Line item was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
# DELETE /line_items/1
# DELETE /line_items/1.json
def destroy
#line_item.destroy
respond_to do |format|
format.html { redirect_to line_items_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_line_item
#line_item = LineItem.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def line_item_params
params.require(:line_item).permit(:product_id)
end
end
And this is my StoreController, containing the index action triggered by the manual refresh:
class StoreController < ApplicationController
include CurrentCart
before_action :set_cart
def index
#products = Product.order(:title)
end
end
It seems that when I empty the cart and inspect the div, the display: none attribute is applied (as it should be) from 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">
<%= hidden_div_if(#cart.line_items.empty?, id: 'cart') do %>
<%= render #cart %>
<% end %>
<ul>
<li>Home</li>
<li>Questions</li>
<li>News</li>
<li>Contact</li>
</ul>
</div>
<div id="main">
<%= yield %>
</div>
</div>
</body>
</html>
The hidden_div_if method is in ApplicationHelper.rb:
module ApplicationHelper
def hidden_div_if(condition, attributes = {}, &block)
if condition
attributes["style"] = "display: none"
end
content_tag("div", attributes, &block)
end
end
But when I inspect the cart div after adding an item, display: none is still applied. What am I missing?
If it matters, here's my CartsController:
class CartsController < ApplicationController
before_action :set_cart, only: [:show, :edit, :update, :destroy]
rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart
# GET /carts
# GET /carts.json
def index
#carts = Cart.all
end
# GET /carts/1
# GET /carts/1.json
def show
end
# GET /carts/new
def new
#cart = Cart.new
end
# GET /carts/1/edit
def edit
end
# POST /carts
# POST /carts.json
def create
#cart = Cart.new(cart_params)
respond_to do |format|
if #cart.save
format.html { redirect_to #cart, notice: 'Cart was successfully created.' }
format.json { render :show, status: :created, location: #cart }
else
format.html { render :new }
format.json { render json: #cart.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /carts/1
# PATCH/PUT /carts/1.json
def update
respond_to do |format|
if #cart.update(cart_params)
format.html { redirect_to #cart, notice: 'Cart was successfully updated.' }
format.json { render :show, status: :ok, location: #cart }
else
format.html { render :edit }
format.json { render json: #cart.errors, status: :unprocessable_entity }
end
end
end
# DELETE /carts/1
# DELETE /carts/1.json
def destroy
#cart.destroy if #cart.id == session[:cart_id]
session[:cart_id] = nil
respond_to do |format|
format.html { redirect_to store_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_cart
#cart = Cart.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def cart_params
params[:cart]
end
def invalid_cart
logger.error "Attempt to access invalid cart #{params[:id]}"
redirect_to store_url, notice: 'Invalid cart'
end
end
After a couple of days of spinning my wheels, I finally noticed that the jQuery selectors in my create.js.erb were missing the #, i.e.:
if ($('cart tr').length == 1) {
$('cart').show('blind', 1000);
}
Instead of:
if ($('#cart tr').length == 1) {
$('#cart').show('blind', 1000);
}
Adding them fixed it.
Related
I have a problem with HTML sortable update in rails 6
I want to drag and drop the images through web page and that is worked fine but, When I want to update the order I got 404 not found error in console log and also an error in find ID.
by the way, I use this script for HTML sortable
https://github.com/jordanhudgens/devcamp-portfolio/blob/master/app/assets/javascripts/html.sortable.js
Controller :
class PortfoliosController < ApplicationController
before_action :set_portfolio_item, only:[:edit , :show, :update , :destroy]
layout "portfolio"
access all: [:show, :index , :angular], user: {except: [:destroy, :new , :create , :update , :edit , :sort]}, site_admin: :all
def index
# You can filter what you want to show in protfolio
# Portfolio.where(subtitle: "Angular")
# You can define it in models and call the method here
# It must create on Portfolio.rb in models
# You can create scope too on models and call
#portfolio_items = Portfolio.by_position
end
def angular
#angular_portfolio_items = Portfolio.angular
end
def sort
params[:order].each do |key, value|
Portfolio.find(value[:id]).update(position: value[:position])
end
head :ok
end
def new
#portfolio_item = Portfolio.new
3.times { #portfolio_item.technologies.build }
end
def create
#portfolio_item = Portfolio.new(portfolio_params)
respond_to do |format|
if #portfolio_item.save
format.html { redirect_to portfolios_path, notice: 'Portfolio was successfully created.' }
else
format.html { render :new }
end
end
end
def edit
end
def update
respond_to do |format|
if #portfolio_item.update(portfolio_params)
format.html { redirect_to portfolios_path, notice: 'The record was successfully updated.' }
else
format.html { render :edit }
end
end
end
def show
end
def destroy
# Perform the lookup
# Destroy/delete the record
#portfolio_item.destroy
#Redirect
respond_to do |format|
format.html { redirect_to portfolios_url, notice: 'Portfolio was successfully deleted!.' }
end
end
private
def portfolio_params
params.require(:portfolio)
end
def set_portfolio_item
#portfolio_item = Portfolio.find(params[:id])
end
end
This is my routes.rb
Rails.application.routes.draw do
devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout', sign_up: 'register' }
resources :portfolios, except: [:show] do
put :sort, on: :collection
end
get 'portfolio/:id' , to: 'portfolios#show', as: 'portfolio_show'
get 'angular-items' , to: 'portfolios#angular'
# we can pass anything here after get
get 'about', to: 'pages#about'
get 'contact', to: 'pages#contact'
resources :blogs do
member do
get :toggle_status
end
end
resources :posts
root to: 'pages#home'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
This is my CoffeeScript for HTML sortable
ready = undefined
set_positions = undefined
set_positions = ->
$('.card').each (i) ->
$(this).attr 'data-pos', i + 1
return
return
ready = ->
set_positions()
$('.sortable').sortable()
$('.sortable').sortable().bind 'sortupdate', (e, ui) ->
updated_order = []
set_positions()
$('.card').each (i) ->
updated_order.push
id: $(this).data('id')
position: i + 1
return
$.ajax
type: 'PUT'
url: '/portfolios/sort'
data: order: updated_order
return
return
$(document).ready ready
Thanks for your helps!
_form.html.erb
<div class="form-group">
<%= f.label :description %><br>
<%= f.select(:description, options_for_select([['', ''],['METRO', 'METRO'], ['BUS', 'BUS'], ['TAXI', 'TAXI'], ['OTHERS', 'OTHERS']]), {}, {class: "form-control", id: "expense_description"}) %>
<br>
<div id="otherDesc">
<%= f.text_field :description_other, class: "form-control" %>
</div>
</div>
index.html.erb
<% #expenses.each do |expense| %>
<tr class="tr-<%= cycle('odd', 'even') %>">
<td class="col-1"><%= (expense.description_other.present? ? expense.description_other : expense.description) %></td>
</tr>
<% end %>
expenses_controller.rb
class ExpensesController < ApplicationController
before_action :set_expense, only: [:show, :edit, :update, :destroy]
# GET /expenses
# GET /expenses.json
def index
#expenses = Expense.all
end
# GET /expenses/1
# GET /expenses/1.json
def show
end
# GET /expenses/new
def new
if Expense.last.present?
#expense = Expense.last.dup
else
#expense = Expense.new
end
end
# GET /expenses/1/edit
def edit
end
# POST /expenses
# POST /expenses.json
def create
#expense = Expense.new(expense_params)
respond_to do |format|
if #expense.save
format.html { redirect_to #expense, notice: 'Expense was successfully created.' }
format.json { render :show, status: :created, location: #expense }
else
format.html { render :new }
format.json { render json: #expense.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /expenses/1
# PATCH/PUT /expenses/1.json
def update
respond_to do |format|
if #expense.update(expense_params)
format.html { redirect_to #expense, notice: 'Expense was successfully updated.' }
format.json { render :show, status: :ok, location: #expense }
else
format.html { render :edit }
format.json { render json: #expense.errors, status: :unprocessable_entity }
end
end
end
# DELETE /expenses/1
# DELETE /expenses/1.json
def destroy
#expense.destroy
respond_to do |format|
format.html { redirect_to expenses_url, notice: 'Expense was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_expense
#expense = Expense.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def expense_params
params.require(:expense).permit(:description, :description_other)
end
end
expense.js
$(document).ready(function () {
$('#expense_description').on('change',function(){
var selectedValue = $(this).val();
selectedValue == "OTHERS" ? $("#otherDesc").show() : $("#otherDesc").hide()
});
});
general.scss
#otherDesc {
display:none;
}
Everything works fine except my index page where I get two values for 'OTHERS' selected option as OTHERS + MY OWN DESCRIPTION. For example in the image it is OTHERS WHITE GLUE. But I would like to have only WHITE GLUE as the description.
Please find attached the image for your reference.
I have tried too hard but unable to get the desired result.
Any suggestions are most welcome.
Thank you in advance.
Found it - I'm kind of blind sometimes...
index.html.erb
<td class="col-1"><%= expense.description %> <%= link_to expense.description_other,{}, {:style => 'color: #CC3366'} %></td>
You've got two <%= ... %>'s in there ...
<%= expense.description %>
And then
<%= link_to expense.description_other,{}, {:style => 'color: #CC3366'} %>
Get ride of the first one when OTHER is selected, using an if conditional(the trinary operator we talked about <expression> ? <if true do this happens>: <if false this happens> The conditional would have to contain both statements inside one <%= ... %> block.
Also, you have an issue here ... should have a conditional of some sort ... probably the ||= & drop the Expense.new or the Expense.last.dup
def new
#expense = Expense.new
#expense = Expense.last.dup
end
i'm currently building a web app, and I encounters some problems.
I want the map display posts that are pushed (so push == true). For that, i'd try a lot of conditions, but it's never working. I feel I am not far from the goal, but for now, the map no reference points.
posts controller :
class PostsController < ApplicationController
before_action :authenticate_user!
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :owned_post, only: [:edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
#posts = Post.all.order("push_updated_at DESC")
if #posts.push == true
#hash = Gmaps4rails.build_markers(#posts) do |post, marker|
marker.lat post.latitude
marker.lng post.longitude
end
end
end
# GET /posts/1
# GET /posts/1.json
def show
end
# GET /posts/new
def new
#post = current_user.posts.build
end
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /posts.json
def create
#post = current_user.posts.build(post_params)
respond_to do |format|
if #post.save
#post.update(push: false)
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
#post = Post.find(params[:id])
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
else
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:user_id, :title, :description, :image, :push, :push_updated_at, ingredients_attributes: [:id, :name, :_destroy])
end
def owned_post
unless current_user == #post.user
flash[:alert] = "That post doesn't belong to you!"
redirect_to root_path
end
end
end
views/posts/index :
<div class="title text-center">
<h1>Alors ? On mange quoi ?</h1>
</div>
<br>
<%= render 'map' %>
<%= render 'post' %>
& views/posts/map :
<script src="//maps.google.com/maps/api/js?v=3.18&sensor=false&client=&key=&libraries=geometry&language=&hl=®ion="></script>
<script src="//google-maps-utility-library-v3.googlecode.com/svn/tags/markerclustererplus/2.0.14/src/markerclusterer_packed.js"></script>
<script src='//google-maps-utility-library-v3.googlecode.com/svn/tags/infobox/1.1.9/src/infobox_packed.js' type='text/javascript'></script> <!-- only if you need custom infoboxes -->
<div style='width: 800px;'>
<div id="map" style='width: 800px; height: 400px;'></div>
<script type="text/javascript">
handler = Gmaps.build('Google');
handler.buildMap({ provider: {}, internal: {id: 'map'}}, function(){
markers = handler.addMarkers(<%=raw #hash.to_json %>);
handler.bounds.extendWith(markers);
handler.fitMapToBounds();
});
</script>
</div>
</div>
So if you have any suggestions about that, you're welcome !!
For this stage you should create a scope on model Post
class Post < ActiveRecord::Base
scope :push, ->{ where(push: true).order("push_updated_at DESC") }
end
and then in controller
def index
#posts = Post.push #this will fetch the post with push true and sort it
#hash = Gmaps4rails.build_markers(#posts) do |post, marker|
#rest of your code
end
Every time I hit submit in my rails app it sends the request 2x. I can get rid of the second partial by hitting refresh. This is a nested app. Todo has_many Items. I included the controllers and the create and the partials.
I included a photo to make a bit more clear.
create.js.erb
$('.todo-items').prepend("<%= escape_javascript(render(#item)) %>");
Items Controller:
class ItemsController < ApplicationController
before_action :set_item, only: [:show, :edit, :update, :destroy]
before_action :set_todo
respond_to :html, :js
# GET /items
# GET /items.json
def index
#items = Item.all
end
# GET /items/1
# GET /items/1.json
def show
#item = Item.find(params[:id])
end
# GET /items/new
def new
#item = #todo.items.build
end
# GET /items/1/edit
def edit
#item = Items.find(params[:id])
end
# POST /items
# POST /items.json
def create
#item = #todo.items.build(item_params)
respond_with(#item) do |format|
if #item.save
format.html { redirect_to [#todo], notice: 'Item was successfully created.' }
format.json { render :show, status: :created, location: #item }
else
format.html { render :new }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /items/1
# PATCH/PUT /items/1.json
def update
#item = Item.find(params[:id])
respond_to do |format|
if #item.update(item_params)
format.html { redirect_to [#todo, #item], notice: 'Item was successfully updated.' }
format.json { render :show, status: :ok, location: #item }
else
format.html { render :edit }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
# DELETE /items/1
# DELETE /items/1.json
def destroy
#item = Item.find(params[:id])
#item.destroy
respond_to do |format|
format.html { redirect_to items_path }
format.json { head :no_content }
format.js { render layout: false }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_item
#item = Item.find(params[:id])
end
def set_todo
#todo = Todo.find(params[:todo_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def item_params
params.require(:item).permit(:content, :todo_id)
end
end
Todos Controller
class TodosController < ApplicationController
respond_to :html, :js
before_action :set_todo, only: [:show, :edit, :update, :destroy]
# GET /todos
# GET /todos.json
def index
#todos = Todo.all
#todo = Todo.new
end
# GET /todos/1
# GET /todos/1.json
def show
#todo= Todo.find(params[:id])
end
# GET /todos/new
def new
#todo = Todo.new
#3.times{#todo.items.build}
end
# GET /todos/1/edit
def edit
end
# POST /todos
# POST /todos.json
def create
#todo = Todo.new(todo_params)
##todo.items.build
respond_to do |format|
if #todo.save
format.html { redirect_to todo_path, notice: 'Todo was successfully created.' }
format.json { render :show, status: :created, location: #todo }
else
format.html { render :new }
format.json { render json: #todo.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /todos/1
# PATCH/PUT /todos/1.json
def update
#todo = Todo.find(params[:id])
respond_to do |format|
if #todo.update(todo_params)
format.html { redirect_to todos_url, notice: 'Todo was successfully updated.' }
format.json { render :show, status: :ok, location: #todo }
else
format.html { render :edit }
format.json { render json: #todo.errors, status: :unprocessable_entity }
end
end
end
# DELETE /todos/1
# DELETE /todos/1.json
def destroy
#todo.destroy
#todo.items.destroy
respond_to do |format|
format.html { redirect_to todos_url, notice: 'Todo was successfully destroyed.' }
format.json { head :no_content }
end
end
def todo_completed
#todo = Todo.find(params[:id])
#todo.completed = true
if #todo.save
flash[:notice] = "Task was completed."
else
flash[:error] = "There was an error completing the task."
end
#redirect_to tasks_path
respond_with(#todo) do |f|
f.html { redirect_to todos_path }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_todo
#todo = Todo.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def todo_params
params.require(:todo).permit(:title, :completed, items_attributes: [:content,:completed, :_destroy])
end
end
This is the show page the one in the photo for Todo. It is rendering the items underneath that specific todo.
<p>Here are all the things you need to complete</p>
<div class="todo-items">
<%= render partial: 'items/item' %>
</div>
<br>
<div class='new-item'>
<%= render 'items/form'%>
</div>
<%= link_to 'Back', todos_path %>
Items/item
<p>todo/show this is the partial <%= #todo.title %></p>
<table class='table table-bordered'>
<thead>
<tr>
<th>Description</th>
<th> Time Left </th>
<th> Mark Complete </th>
</tr>
</thead>
<tbody>
<% #todo.items.each do |item| %>
<tr>
<td><%= link_to item.content, [#todo, item] %></td>
<td><%= item.days_left %>
<td>
<%= link_to todo_item_path(#todo,item), method: :delete, data: { confirm: 'Are you sure?' }, remote: true, class: 'delete_item' do %>
<i class="fa fa-check"></i>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
<br>
<p>end of partial item</p>
Item/form
<p>
<strong>Todo:</strong>
<%= #todo.title %>
</p>
<%= form_for [#todo, #todo.items.build], remote: true do |f| %>
<div class="field">
<%= f.label :content %><br>
<%= f.text_field :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
A little more info from the console. It starts when I click on the list(Groceries) and end with me hitting submit to add the item
Started GET "/todos/25" for 127.0.0.1 at 2014-10-08 19:19:58 -0500
Processing by TodosController#show as HTML
Parameters: {"id"=>"25"}
Todo Load (0.3ms) SELECT "todos".* FROM "todos" WHERE "todos"."id" = ? LIMIT 1 [["id", 25]]
CACHE (0.0ms) SELECT "todos".* FROM "todos" WHERE "todos"."id" = ? LIMIT 1 [["id", "25"]]
Item Load (0.4ms) SELECT "items".* FROM "items" WHERE "items"."todo_id" = ? [["todo_id", 25]]
Rendered items/_item.html.erb (42.9ms)
Rendered items/_form.html.erb (17.3ms)
Rendered todos/show.html.erb within layouts/application (66.6ms)
Completed 200 OK in 346ms (Views: 339.5ms | ActiveRecord: 1.3ms)
Started POST "/todos/25/items" for 127.0.0.1 at 2014-10-08 19:20:12 -0500
Processing by ItemsController#create as JS
Parameters: {"utf8"=>"✓", "item"=>{"content"=>"Cereal"}, "commit"=>"Create Item", "todo_id"=>"25"}
Todo Load (0.2ms) SELECT "todos".* FROM "todos" WHERE "todos"."id" = ? LIMIT 1 [["id", 25]]
(0.3ms) begin transaction
Todo Load (0.2ms) SELECT "todos".* FROM "todos" WHERE "todos"."id" = ? LIMIT 1 [["id", 25]]
SQL (0.5ms) INSERT INTO "items" ("content", "created_at", "todo_id", "updated_at") VALUES (?, ?, ?, ?) [["content", "Cereal"], ["created_at", "2014-10-09 00:20:12.060569"], ["todo_id", 25], ["updated_at", "2014-10-09 00:20:12.060569"]]
(148.1ms) commit transaction
Item Load (0.2ms) SELECT "items".* FROM "items" WHERE "items"."todo_id" = ? [["todo_id", 25]]
Rendered items/_item.html.erb (9.8ms)
Rendered items/create.js.erb (12.5ms)
Completed 200 OK in 180ms (Views: 15.2ms | ActiveRecord: 149.4ms)
The problem is with your create.js.erb file. You have below code:
$('.todo-items').prepend("<%= escape_javascript(render(#item)) %>");
In this code you are pre-pending html in todo-items you should replace items each time instead. like below :
$('.todo-items').html("<%= escape_javascript(render(#item)) %>");
This will replace html each time with new html.
So I worked through the Agile Web Development with Rails book, and had a nice functional web application running on Heroku based on the "Depot" app in the book. I was editing CSS/SASS primarily, and I did something that messed up my line_item quantity in my cart.
For the life of me I can't figure out what I've done. Now when I click "Add to cart" button on my main page, 2 items are added to my cart. Does this have something to do with my database? I'm totally flummoxed after losing a bit of my flow in photoshop/CSS-land.
So in a nutshell:
I hit the "Add to cart" button, and I get two of each item I click on.
Here is my carts controller:
class CartsController < ApplicationController
skip_before_filter :authorize, only: [:create, :update, :destroy]
# GET /carts
# GET /carts.json
def index
#carts = Cart.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #carts }
end
end
# GET /carts/1
# GET /carts/1.json
def show
begin
#cart = Cart.find(params[:id])
rescue ActiveRecord::RecordNotFound
logger.error "Attempt to access invalid cart #{params[:id]}"
redirect_to store_url, notice: 'Invalid cart'
else
respond_to do |format|
format.html #show.html.erb
format.json { render json: #cart }
end
end
end
# GET /carts/new
# GET /carts/new.json
def new
#cart = Cart.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #cart }
end
end
# GET /carts/1/edit
def edit
#cart = Cart.find(params[:id])
end
# POST /carts
# POST /carts.json
def create
#cart = Cart.new(params[:cart])
respond_to do |format|
if #cart.save
format.html { redirect_to #cart, notice: 'Cart was successfully created.' }
format.json { render json: #cart, status: :created, location: #cart }
else
format.html { render action: "new" }
format.json { render json: #cart.errors, status: :unprocessable_entity }
end
end
end
# PUT /carts/1
# PUT /carts/1.json
def update
#cart = Cart.find(params[:id])
respond_to do |format|
if #cart.update_attributes(params[:cart])
format.html { redirect_to #cart, notice: 'Cart was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #cart.errors, status: :unprocessable_entity }
end
end
end
# DELETE /carts/1
# DELETE /carts/1.json
def destroy
#cart = current_cart
#cart.destroy
session[:cart_id] = nil
respond_to do |format|
format.html { redirect_to store_url }
format.json { head :no_content }
end
end
end
Here is my models/cart.rb file:
class Cart < ActiveRecord::Base
attr_accessible :title, :body, :name, :quantities_attributes, :quantities, :quantity, :product_id, :line_items
#the addition of everything in attr_accessible after :body may be unnecessary if bugs show up later on...
has_many :line_items, dependent: :destroy
def add_product(product_id)
current_item = line_items.find_by_product_id(product_id)
if current_item
current_item.quantity += 1
else
current_item = line_items.build(product_id: product_id)
end
current_item
end
def total_price
line_items.to_a.sum { |item| item.total_price }
end
end
Here is my create.js.erb file
$("#notice").hide();
if ($('#cart tr').length == 1) { $('#cart').show('blind', 1000); }
$('#cart').html("<%=j render #cart %>");
$('#current_item').css({'background-color':'#fb0f19'}).
animate({'background-color':'#08BBD1'}, 1000);
Here is my application_controller.rb:
class ApplicationController < ActionController::Base
before_filter :authorize
protect_from_forgery
private
def current_cart
Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
cart = Cart.create
session[:cart_id] = cart.id
cart
end
protected
def authorize
unless User.find_by_id(session[:user_id])
redirect_to login_url, notice: "Please log in"
end
end
end
Here is my views/carts/_cart.html.erb
<% unless cart.line_items.empty? %>
<div class="cart_title">Your Comic Cart</div>
<table>
<%= render(cart.line_items) %>
<tr class="total_line">
<td colspan="2">Total</td>
<td class="total_cell"><%= cart.total_price %> BTC</td>
</tr>
</table>
<%= button_to "Checkout", new_order_path, method: :get %>
<%= button_to 'Empty cart', cart, method: :delete,
confirm: 'Are you sure?' %>
<% end %>
line_items_controller.rb:
class LineItemsController < ApplicationController
skip_before_filter :authorize, only: :create
# GET /line_items
# GET /line_items.json
def index
#line_items = LineItem.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #line_items }
end
end
# GET /line_items/1
# GET /line_items/1.json
def show
#line_item = LineItem.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #line_item }
end
end
# GET /line_items/new
# GET /line_items/new.json
def new
#line_item = LineItem.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #line_item }
end
end
# GET /line_items/1/edit
def edit
#line_item = LineItem.find(params[:id])
end
# POST /line_items
# POST /line_items.json
def create
#cart = current_cart
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.js { #current_item = #line_item }
format.json { render json: #line_item,
status: :created, location: #line_item }
else
format.html { render action: "new" }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
# PUT /line_items/1
# PUT /line_items/1.json
def update
#line_item = LineItem.find(params[:id])
respond_to do |format|
if #line_item.update_attributes(params[:line_item])
format.html { redirect_to #line_item, notice: 'Line item was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
# DELETE /line_items/1
# DELETE /line_items/1.json
def destroy
#line_item = LineItem.find(params[:id])
#line_item.destroy
respond_to do |format|
format.html { redirect_to line_items_url }
format.json { head :no_content }
end
end
end
I'm not sure if there are more files worth showing, it is late and I'm super lost, so I probably should have waited until morning to post this. Let me know if anyone else has run into this. I still have a hard time with databases, so I could be completely missing the right place to be looking. Thanks for any help, I know this misstep lacks focus in my explination.
I think the problem is in your store.js.coffee. Check and modify the code as per errata 153 from below link:
http://pragprog.com/titles/rails4/errata
i.e change $(document).on "ready page:change" to $(document).on "ready, page:change"