small medium large xlarge


ys says:

what i did was

1. add format.js in carts controller destroy method

2. add remote: true on empty button

3. create destroy.js.erb in views/carts with

$('#cart').html("<%=j render @cart %>").hide('blind', 1000);

it works fine but seems not following given hint…

suke says:

Does the cart appear when you access the page at first?


Does the cart appear after you empty the cart and reload the page?

michael says:

@ys: in your 3rd step i did this:

$('#cart tr').not('.total_line').remove();
$('#cart').hide('blind', 1000);

jack says:

@michael: why do we need to use the .not. I had used only the #cart.hide but that did not work after emptying the cart and then adding to it again. However, your solution works great. Can you explain this a little more please?

Alex says:

@jack: The reason you use the not() is to remove all tr entires from the table, except for #total_line. The reason you want to do this is because in create.js.erb we told the browser to show() the cart if, and only if the number of tr entires under #cart is 1.

In other words, when you click Add to Cart button, the very first thing that happens is the JavaScript checking to see how many tr elements available (length). If the length == 1, we call show(); otherwise we don’t. If we were to remove total_line, the result, after removal, of length would be 0*.

The reason your method doesn’t work is because after clearing, length equals 0. When you then push Add to Cart, the *show() is skipped, and we render @cart. What show() does is essentially revert what the changes we did with hidden_div_if() helper, setting display: none. Since we did no re-fetch application.html.erb, but rather only ask to fetch _cart.html.erb, via the 2nd line in create.js.erb, the logic of hidden_div_if() is never run on the server, and we never run it on the browser either, because length = 0 after we removed total_line.

TL;DR you need total_line so that JQuery’s show() will execute, removing display:none which was set via Empty Cart.

frog says:

@ys: can you (or someone else) insert the code for the first two steps, as well. I understand what you are saying, but don’t have the code correctly written

Shaun says:

@frog this is what I did. 1) Add this to carts_controller.rb under def destroy

format.js { }

2. Then in _cart.html.erb I added remote: true to the Empty cart button.

3. Created a destroy.js.erb in the view/carts dir with the following code:

if ($('#cart').length == 1) { $('#cart').hide("blind", {direction: "vertical" }, 1000); }

$('#cart tr').not('.total_line').remove();

That’s it and it worked make sure to clear your browser cache before testing.

monofu. says:

I checked my solution with @frog’s but couldn’t find any difference. I eventually found it. There was an error in my brackets in my if loop. For some reason it didn’t evaluate. To solve this I just did the following:
cart = $('#cart');
That way the cart div is cached in a variable and makes the if loop a lot easier write and read, and uses slightly less memory (emphasis on slightly).

Bess says

Not sure why we want the extra tr hanging around, if just to make the cart display work. My destroy.js.erb code does a simple hide as long as there are some items in the cart:

if ($('#cart tr').length > 0) { $('#cart').hide('blind', 1000); }  
and then in the display I just show the cart if its already hidden:
if ($('#cart tr').is(':hidden')) { $('#cart').show('blind', 1000); } 

Seems easier to keep track of the whole cart either being shown or hidden, rather than removing DOM elements and checking against table row length..

Doug asks

Why delete rows from the table with .not? If you are hiding the table, does it matter? If it does matter, just removing some rows results in a table that has no entries, but a total cost of whatever you last had (viewable in the page source). Rendering the cart first, then hiding it ensures that the table is correct. The following worked for me:
$('#cart').html("<%= j render @cart %>");

Deny say

Syntax of jQuery function .hide() is difference: .show( [duration],[easing],[callback]). But better solution is function .slideUp([duration], [easing], [callback]).

Ilya says

I am getting POST http://localhost:3000/carts/34 500 (Internal Server Error) when switching to AJAX, not sure why, suggestions?

Ok I got it I put destroy.js.erb into line items instead of cart.

Joe says

I still don’t get why we need extra lines to remove tr’s from the table. I think ys’s solution is better. Upon clicking Empty button, it’ll render the cart partial again so the number of tr’s are changed, thus no need to remove it manually.

Malcolm says

I think part of the confusion is a result of the not-quite-correct reasoning given in the chapter earlier regarding these two lines for adding items to the cart (in lineitems/create.js.erb):

if ($('#cart tr').length == 1) { $('#cart').show('blind', 1000); }

$('#cart').html("<%= escape_javascript render(@cart) %>");

The if statement is supposed to show the cart when the first product is added, the reasoning given is that it’s checking if there is one row (one tr) for the product in the table. In actual fact the real and only reason it displays is because the row for the total already exists; there is no row for the product yet because it gets rendered when that second line is called. You can test this by commenting out or removing the second line, the cart will still show (but will look empty) when you add a product (aka. create a line item).

That’s also the reason why putting the if line below the other line will break it, even though it’s possibly the more correct place: there would in fact be two rows (one for the product and one for the total) so the condition would have to be changed to .length == 2.

The code that’s provided still works of course, but in this exercise it means you have to be specific about leaving the total row there to satisfy the if statement condition. Combining the not() and remove() methods seem a bit of a hacky way to do it, so I agree with others and think using render is cleaner. However, why repeat yourself (DRY principle)? If you change the condition to look for 2 rows, and stick that if statement under the second line instead of above it, you can just use

$('#cart').hide('blind', 1000);
in carts/destroy.js.erb and be done with it (after doing step 1 & 2 in ys’ post at the top of the page). That’s just my humble opinion.

Ian says

@Malcolm, that almost works, but there’s a problem when adding an additional unit of the only item in the cart. The if clause triggers and the cart ‘blinds’ down again.

Adding the following to the if resolves the problem:

&& $('#cart').css('display') == 'none'

Michael says

@Malcolm, I totally agree with you. In fact, I would go a step further and question why we don’t just test for the number of items in the cart? For example:

$('#cart').html("<%= escape_javascript render(@cart) %>");

if (<%= @cart.line_items.length == 1 %>) { $('#cart').show('blind', 1000); }

$('#current_item').css({'background-color':'#88ff88'}).animate({'background-color':'#114411'}, 1000);

Then in the destroy.js.erb file we can just .hide(“blind”) the div and be done with it.

Juank says

I was bothered by the fact that removing the elements first and hiding the cart later was still looking like a cut scene. So my solution was:

$('#cart').hide('blind', 500, function() {
    $('#cart tr:not(.total_line)').remove();
Page History
  • V43: Juan Carlos Pazmino [18 days ago]
  • V42: Juan Carlos Pazmino [18 days ago]
  • V41: Michael Guren [over 3 years ago]
  • V39: Ian McNaney [over 3 years ago]
  • V40: System [over 3 years ago]
  • V41: Malcolm McKeown [almost 4 years ago]
  • V40: Malcolm McKeown [almost 4 years ago]
  • V39: Malcolm McKeown [almost 4 years ago]
  • V38: Malcolm McKeown [almost 4 years ago]
  • V37: Malcolm McKeown [almost 4 years ago]