Pt-F-2-31
I’ll just leave my notes here:
The button_to that worked for me:
In my decrement method, I had if @line_item.update_attributes(params[:id]) which contained respond_to. Turns out this didn’t work if the line_item should disappear because if you run @line_item.destroy BEFORE update_attributes, you get a “frozen hash” error that not only returns false but returns a runtime error. I got around this with the following:
if @line_item.quantity == 0
@line_item.destroy
end
begin @line_item.update_attributes(params[:line_item])
rescue
end
respond_to do |format|
format.js {@current_item = @line_item}
end
Also, turns out that the book uses a little hack to make the jquery blind methods go. I had to use the following to get the cart to disappear when the last item had been decremented out of existence:
$(’#cart’).html(”<%= j render @cart %>”); if ($(’#cart tr’).length 1) { $('#cart').hide('blind', 1000); } $('#current_item').css({'background-color':'#88ff88'}).animate({'background-color':'#114411'}, 1300);
When I had if ($('#cart tr').length 0, it would never work because the cart grand total is inside a tr that never goes away. This wasn’t made obvious when we were building our create.js.erb in the example so it took me a bit to figure out.
I am a little glad I learned so much, but mostly just angry right now.
Christian says:
Hi there, could you do me a big favour and share the complete code? I’ve been fiddling around with this task for hours but I just can’t get it to work (the ajax part).
Johan says:
I believe the button_to should be: <%= button_to ‘Del’, decrement_line_items_path(line_item_id: line_item), remote: true %> I.e “decrement_line_items(!)_path”, at least that worked for me.
Tiger says:
Should this decrement function be written in the model or the controller?
Exl says:
1. Create a route:
resources :line_items do
#member do
# put 'decrement'
#end
put 'decrement', on: :member
end
Note: you can use either of the two ways: member do/end or put/on. But member do/end is for multiple routes, so just stick with put/on in this case. Read: http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
2. Add a decrement button (the value of mine is ‘V’, i.e. like a down arrow). Because of the above route, we get a helper called path (decrement_line_item_path).
<td><%= button_to 'V', decrement_line_item_path(line_item), method: :put, remote: true %></td>
3. Create the ‘decrement’ method in line_items_controller.rb (again, all thanks to the route).
# PUT /line_items/1
# PUT /line_items/1.json
def decrement
@cart = current_cart
# 1st way: decrement through method in @cart
@line_item = @cart.decrement_line_item_quantity(params[:id]) # passing in line_item.id
# 2nd way: decrement through method in @line_item
#@line_item = @cart.line_items.find_by_id(params[:id])
#@line_item = @line_item.decrement_quantity(@line_item.id)
respond_to do |format|
if @line_item.save
format.html { redirect_to store_path, notice: 'Line item was successfully updated.' }
format.js {@current_item = @line_item}
format.json { head :ok }
else
format.html { render action: "edit" }
format.js {@current_item = @line_item}
format.json { render json: @line_item.errors, status: :unprocessable_entity }
end
end
end
Again, there is more than one way to do it, I show two above, but another still would be to put all the logic into this controller method (though that would go against the point of MVC). I vote for the 1st way because it is less roundabout (i.e. one less call).
Note: I don’t know what is the right thing to do for the else-clause in in respond_to. Please reply if you know.
Here is the 1st way method, in cart.rb:
def decrement_line_item_quantity(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
Here is the 2nd way method, in line_item.rb:
def decrement_quantity(line_item_id)
current_item = LineItem.find_by_id(line_item_id)
if current_item.quantity > 1
current_item.quantity -= 1
else
current_item.destroy
end
current_item
end
4. Add the js.erb partial. Because the method in line_items_controller is called ‘decrement’, the partial will be /app/views/line_items/decrement.js.erb and it will contain:
$('#cart').html("<%=j render @cart %>");
$('#current_item').css({'background-color':'#88ff88'}).animate({'background-color':'#114411'}, 1000);
This renders the cart, and then flashes the item that was decremented.

