However, the user can still remove individual line items from the cart emptying the cart this way…

bryan says:
Just ad if not @checkout to the button and set the @checkout to true in the checkout action of the store controller.

<%= button_to "Check out", :action => :checkout if not @checkout -%>

Eric asks:

Why does this works ? Reading ths code I see that button is disabled if order is empty. But when you are on the checkout page your cart isnt empty…So can anyone explain me why does this work ?

k9d adds:

I prefer the ilari’s method for stopping the user from checking out again over the rest because it doesn’t change the interface around on the user (the buttons remain, but are disabled) and the user can still see what they have in their cart.

Also I think it’s good to allow users to remove items from their cart at the checkout stage—many times it’s not until I check out that I realize I have two or something I thought I had only one of, etc. I tested ilari’s method and removing a cart item before hitting “Place Order” works great (only the items listed in the cart when you hit “Place Order” show up in the line_items in the database).

However, if the user empties the cart to zero items, they’re left staring at the checkout page with no way to cancel their order. This could be remedied a few different ways (“Cancel Order” button returning to index, automatically returning to index when there’s no items in the cart ala blind_up).

k9d asks:

There are plenty of answers on this page that solve this problem by restricting user behavior, but why not nip the problem in the bud and change the behavior of the “Checkout” button so that it only blanks the field out when no data has been previously entered? I’m having trouble getting that to work because of the trickery involved with the RAM copy of @order VS the one that gets saved to the database, can anyone guide me there?

Dave says:

One way to hide the checkout_button is to set a variable:

@hide_checkout_button = true

You can then test for it in the layout:

<% if @hide_checkout_button -%>
...
<% end -%>

Because instance variable default to the value nil when they haven’t been assigned, this works fine if the non-checkout actions do not set the value.

You can take this a step further and set the variable inside the checkout view instead:

<% @hide_checkout_button = true -%>

Because the view template is evaluated before the layout, this variable will then be available for inspection in the layout.

Which is better depends on your application flow: what is the logical thing that determines you’re looking at a checkout screen?

Nicolas says:

To me the most straightforward answer to your last question, at this stage of development, is whether or not the @order instance variable is set or not. If it is, there is not much point clicking on “checkout”. This may change further along the process, but my solution is simply this, in _cart.rhtml:

<%= button_to "Checkout", :action => :checkout if @order.nil? %>

David M says:

It would probably make sense to hide the empty cart button as well on the checkout page to prevent a user from logging an empty order.

linoj says:

I had changed the template earlier to use form_remote_tag instead of button_to on Empty cart. So it seems cleaner to wrap both in an if-end (or is that too un-ruby-esque?)

Anthony Ettinger says:

I think that hiding via javascript and disabling the button is not a good solution. What if javascript is disabled? What if the form submits via a text field in the cart added later on (ie: the user hits “enter”).

I think it makes sense to change the “Checkout” button to a “Cancel Order” button if you are in the checkout process…you can use the @order object to determine this in your cart.rhtml template:

I left the cart full, in case they want to add more items, etc. At this point, the user can still empty the cart should they choose.

Side Note: With the ajax-ified “Empty Cart” button working, you are still left on the “Checkout” page if you empty it there. Perhaps “Empty Cart” should always redirect to index. This is negated by implementing a “Cancel Order” button that redirects to index.

Alan says:

There is a problem if you have AJAX click-to-remove on your cart and uses !@order.nil? to disable the buttons – At checkout, the buttons are disabled because the variable @order has been created, but when a user clicks on an item in the cart (to remove it), the cart updates and the buttons are enabled again. This is probably because @order is not present in the remove_from_cart action.

JP says:

Alan, you’re correct. If you keep the AJAX click-to-remove, the !@order.nil? doesn’t work to disable the buttons.

This hides the buttons from view if the @hide_buttons variable is set to true. It works and both buttons do not show when you click checkout. Please let me know if there are problems with this method.

The only potential problems I can see with the @hide_buttons variable solution above are: *you need that variable set in at least on other place (when item is deleted from cart after checkout screen) *that you have logic in your view template (not strictly MVC-esque)

There is the related problem of saving an empty order. The save_order action should check if @cart is empty (like the checkout action does), before saving the order. It is not enough to simply disable or hide the “Empty cart” button, since you can go to the checkout page in one browser window, then open up another one and go to the index page and empty the cart from there. Going back to the checkout page and submitting the form will invoke the save_order action on an empty cart, and lead to bad data in our database (orders with no line_items).

Scott says:

Tom, here is an example of what you suggested re: checking if the cart is empty:

McWild says:

There is another way to disable both buttons while checkout view is shown to the customer.
We can use session for that!!
So customer could change line items quantity and have both buttons(Empty cart, Checkout) disabled anyway until He/She leave the checkout page.

Carnator says:

The button_to helper has an HTML option called :disabled, so if the order is nil, then a negation will tell the :disabled option to activate.

Kedar Mhaswade says:

I think most of the suggestions above work for me as well. Thank you. I also thought that showing “Check Out” and “Empty” buttons when the cart is empty can be avoided too. Thus, my cart partial looks like this: