Rails:更新购物车中的数量

tdo*_*dog 6 javascript ruby ruby-on-rails cart

Ruby新手在这里。我正在通过Rails进行敏捷Web开发。在第11章中,挑战是要在购物车中的商品上添加“减少数量”按钮。我继续尝试并尝试实现增加链接。

问题是当我单击链接时,它什么也没做。

line_items_controller.rb

def decrease
  @cart = current_cart
  @line_item = @cart.decrease(params[:id])

  respond_to do |format|
    if @line_item.save
      format.html { redirect_to store_path, notice: 'Item was successfully updated.' }
      format.js { @current_item = @line_item }
      format.json { head :ok }
    else
      format.html { render action: "edit" }
      format.json { render json: @line_item.errors, status: :unprocessable_entity}
    end
  end
end

def increase
  @cart = current_cart
  @line_item = @cart.increase(params[:id])

  respond_to do |format|
    if @line_item.save
      format.html { redirect_to store_path, notice: 'Item was successfully updated.' }
      format.js   { @current_item = @line_item }
      format.json { head :ok }
    else
      format.html { render action: "edit" }
      format.json { render json: @line_item.errors, status: :unprocessable_entity }
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

cart.rb

def decrease(line_item_id)
  current_item = line_items.find(line_item_id)
  if current_item.quantity > 1
    current_item.quantity -= 1
  else
    current_item.destroy
  end
  current_item
end

def increase(line_item_id)
  current_item = line_items.find(line_item_id)
  current_item.quantity += 1
  current_item
end
Run Code Online (Sandbox Code Playgroud)

routes.rb

resources :line_items do
  put 'decrease', on: :member
  put 'increase', on: :member
end
Run Code Online (Sandbox Code Playgroud)

_line_item.html.erb

<% if line_item == @current_item %>
<tr id="current_item">
<% else %>
<tr>
<% end %>
  <td><%= line_item.quantity %> &times;</td> 
  <td><%= line_item.product.title %></td>
  <td class="item_price"><%= number_to_currency(line_item.total_price) %></td>
  <td><%= link_to "-", decrease_line_item_path(line_item), method: :put, remote: true %></td>
  <td><%= link_to "+", increase_line_item_path(line_item), method: :put, remote: true %></td>
  <td><%= button_to 'Remove Item', line_item, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
Run Code Online (Sandbox Code Playgroud)

/line_items/increase.js.erb

$('#cart').html("<%= escape_javascript(render(@cart)) %>");
$('#current_item').css({'background-color':'#88ff88'}).animate({'background-color':'#114411'}, 1000);
Run Code Online (Sandbox Code Playgroud)

/line_items/decrease.js.erb

$('#cart').html("<%= escape_javascript(render(@cart)) %>");
$('#current_item').css({'background-color':'#88ff88'}).animate({'background-color':'#114411'}), 1000);
if ($('#cart tr').length==1) {
  // Hide the cart
  $('#cart').hide('blind', 1000);
}
Run Code Online (Sandbox Code Playgroud)

让我知道我是否忘记了任何重要事项。提前致谢!

- - 编辑 - -

我将代码更改为Rich发布的内容,这就是单击“ +”链接时控制台中显示的内容。

Started GET "/line_items/25/qty" for ::1 at 2016-01-30 23:49:11 -0600

ActionController::RoutingError (No route matches [GET] "/line_items/25/qty"):
Run Code Online (Sandbox Code Playgroud)

因此,我看到它需要一个数量路由,但我不太确定如何设置它。我猜我们设置的JS警报没有触发,因为它在此时被阻塞了?

----编辑2 ----

现在,我通过POST上下两个链接传递链接,并收到此名称错误:

Started POST "/line_items/25/qty" for ::1 at 2016-01-31 09:49:04 -0600
Processing by LineItemsController#qty as JS
  Parameters: {"id"=>"25"}
Completed 500 Internal Server Error in 38ms (ActiveRecord: 0.0ms)

NameError (undefined local variable or method `current_cart' for #<LineItemsController:0x007fbfb11ea730>):
  app/controllers/line_items_controller.rb:70:in `qty'
Run Code Online (Sandbox Code Playgroud)

让我感到困惑的是,current_cart该方法在increasedecrease方法中有效,而在中却无效qty

Ric*_*eck 3

对于这种类型的模式,您最好将逻辑干燥为单个操作

#config/routes.rb
resources :line_items do
   match :qty, action: :qty, via: [:post, :delete], on: :member #-> url.com/line_items/qty
end

#app/models/line_item.rb
class LineItem < ActiveRecord::Base
   after_update :check_qty, if: "qty_changed?"

   private

   def check_qty
     self.destroy if self.qty.zero?
   end
end 

#app/controllers/line_items_controller.rb
class LineItemsController < ApplicationController
   def qty
      @cart = current_cart
      @item = @cart.line_items.find params[:id]

      if request.post? #-> increment
         method = "increment"
      elsif request.delete? #-> decrement
         method = "decrement"
      end

      @item.send(method, :qty, params[:qty])

      respond_to do |format|
          if @item.save
             format.html { redirect_to store_path, notice: 'Item was successfully updated.' }
             format.js   { @current_item = @line_item }
             format.json { head :ok }
          else
             format.html { render action: "edit" }
             format.json { render json: @line_item.errors, status: :unprocessable_entity}
         end
     end
   end
end
Run Code Online (Sandbox Code Playgroud)

这将允许您将单个链接(可能为qty)传递到您的控制器。如果将其留空,它将仅使用 1 作为数量:

 <%= button_to "+", line_items_qty_path(@line_item), params: { qty: 5 } %>

 <%= link_to "-", line_items_qty_path(line_item), method: :post, remote: true %>
 <%= link_to "+", line_items_qty_path(line_item), method: :delete, remote: true %>
Run Code Online (Sandbox Code Playgroud)

调试

由于您是新手,您需要了解调试

当做这样的事情时,有很多地方可能会“出错”。像许多缺乏经验的开发人员一样,您基本上会说“它不起作用”……问题是许多经验丰富的开发人员知道某个地方一定存在问题。

你能做的最好的事情就是找出哪里出了问题。这是一个繁琐的过程(测试每个部分);你应该从你的 JS 开始:

#app/views/line_items/qty.js.erb
alert("test");
Run Code Online (Sandbox Code Playgroud)

如果上面的内容触发,则意味着您正在做正确的事情

如果您使用我推荐的代码添加上述文件,我们将对可能出现的问题有更好的了解。您还需要发布发送到控制器的请求的控制台日志line_items(这将表明 Rails 是否将请求视为成功)。

一旦发现问题,您就可以确定需要采取哪些措施来解决该问题,这也是许多人期望问题的根源。


顺便说一句,我们之前构建过一个购物车(它使用会话而不是数据库):

在此输入图像描述

如果你愿意的话,我可以写下如何做。它使用基于会话的模型


更新

您看到的错误是因为您GET通过链接发送请求;我的路线分别是POST& DELETE。您需要类似以下内容:

 <%= link_to "-", line_items_qty_path(line_item), method: :post, remote: true %>
Run Code Online (Sandbox Code Playgroud)

我想我在我的帖子中弄错了(抱歉) - 你必须确保你通过了methodasPOSTDELETE


更新2

要初始化该current_cart方法,您需要确保它可用。

Ryan Bates 在他的“会话模型”Railscast中提到了这一点——

#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
   before_filter :current_cart
   helper_method :current_cart

   private

   def current_cart
     @current_cart = session[:cart_id] ? Cart.find(session[:cart_id]) : Cart.create
     session[:cart_id] = @current_cart.id if @current_cart.new_record?
   end
end
Run Code Online (Sandbox Code Playgroud)