mr_muscle
mr_muscle

Reputation: 2900

Rails 6 create order confirmation before submit

I'm trying to create an order confirmation page for my Rails 6 app. The idea is that user will see a preview of the item they are creating before submitting and the object being saved in the database. Below desired flow:

I followed two main threads that describe this type of action, but they are quite old - 11 and 12 years old. Nevertheless based on that I've created below code:

# controllers/cash_transactions/withdrawals_controller.tb

module CashTransactions
  class WithdrawalsController < CashTransactions::BaseController

    (...)

    def confirm
      @cash_transaction = CashTransaction.new(cash_transaction_params)

      render 'cash_transactions/_confirm'
    end
  end
end

# routes.rb

  namespace :cash_transactions do
    resources :withdrawals, only: %i[new create] do
      collection do
        post :confirm
      end
    end
  end

With corresponding views:

# app/views/cash_transactions/new.html.erb

<%= render 'cash_transactions/form', cash_transaction: @cash_transaction %>


# views/cash_transactions/_form
# the form is rendered for cash_transaction create action

<%= simple_form_for cash_transaction, url: { action: :confirm } do |f| %>
  <%= f.input :amount %>
  <%= f.button :submit, 'Submit' %>
<% end %>


# confirmation page under views/cash_transactions/_confirm.html.erb

<div>
  Total of withdrawal: <%= @cash_transaction.amount.to_i %>
</div>

<%= link_to 'Confim', cash_transactions_withdrawals_path(@cash_transaction), method: :post %>
<%= link_to 'Cancel', cash_transactions_path %>

And everything works until the user clicks confirm button in views/cash_transactions/_confirm.html.erb - instead of creating a record an error appears:

param is missing or the value is empty: cash_transaction Did you mean? authenticity_token action controller _method

where did I go wrong? or there is a completely different way to do so?

Upvotes: 0

Views: 658

Answers (1)

melcher
melcher

Reputation: 1601

tl/dr: You need to add parameters to your create request.

Why this is happening

The /confirm view is being rendered with an (unsaved) @cash_transaction object, however that object is not being used and so the information is being lost when the page is rendered.

The line:

<%= link_to 'Confim', cash_transactions_withdrawals_path(@cash_transaction), method: :post %>

Will submit a POST request with no parameters to the /cash_transactions/withdrawals#create (because you've given it no parameters to post). It doesn't know to include the params from the previous request.

There are a few options to fix this... you can add params as URL parameters in link_to like this, however I wouldn't recommend posting with params in the URL.

You can use button_to instead, and pass in the cash_transaction arguments from the previous request in the params: option (or pull them out of the unsaved @cash_transaction object).

Approach #1 - reuse create params

# Get them from the params sent in the previous request. In the controller...
  def create
    @cash_transaction = CashTransaction.create!(cash_transaction_params)
    # etc...
  end
  #...
  protected

  def cash_transaction_params
    params[:cash_transaction].permit(:amount, :whatever)
  end
  helper_method :cash_transaction_params
# In the view
<%= button_to 'Confirm', {action: 'create', params: cash_transaction_params}

Approach #2 - Access attributes from the model you built

<%= button_to 'Confirm', {action: 'create', params: @cash_transaction.attributes.slice('amount', 'other_attribute') }

Or you could do something like render the form again but hidden and have the "confirm" button submit the hidden form (with { action: :create } instead of { action: :confirm}). This last solution is probably the easiest to understand.

Upvotes: 1

Related Questions