Bhav
Bhav

Reputation: 2185

Creating multiple records on one form in Rails app

I've been struggling on this for the last couple of day and some help would be greatly appreciated. A Rails app that has two models:

When creating a new balance at http://localhost:3000/balances/new, the form looks like this:

enter image description here

How can I enable users to be able to create multiple balances at the same time? There should only be one textbox for the date field which should be used for all the balances records created but there should be multiple account dropdown lists and balance textboxes on a form.

I've tried looking up nested forms but I'm struggling.

CODE

schema

  create_table "accounts", force: :cascade do |t|
    t.string   "name"
    t.boolean  "credit"
    t.boolean  "active"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "balances", force: :cascade do |t|
    t.decimal  "balance"
    t.date     "date"
    t.integer  "account_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

account.rb

class Account < ActiveRecord::Base
  has_many :balances

  validates :name, presence: true, length: { maximum: 250 },
                  uniqueness: { case_sensitive: false }
end

balance.rb

class Balance < ActiveRecord::Base
  belongs_to :account
  #default_scope -> { order(date: :desc) }

  validates :account, presence: true, length: { maximum: 250 }
end

accounts_controller.rb

....
def new
  @account = Account.new
end

def create
  @account = Account.new(account_params)

  respond_to do |format|
    if @account.save
      format.html { redirect_to accounts_path, notice: 'Account was successfully created.' }
      format.json { render :show, status: :created, location: @account }
    else
      format.html { render :new }
      format.json { render json: @account.errors, status: :unprocessable_entity }
    end
  end
end
....

balances_controller.rb

....
def new
  @balance = Balance.new
end

def create
  @balance = Balance.new(balance_params)

  respond_to do |format|
    if @balance.save
      format.html { redirect_to balances_path, notice: 'Balance was successfully created.' }
      format.json { render :show, status: :created, location: @balance }
    else
      format.html { render :new }
      format.json { render json: @balance.errors, status: :unprocessable_entity }
    end
  end
end
....

balances/_form.html.erb

<%= form_for(@balance) do |f| %>
  <% if @balance.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@balance.errors.count, "error") %> prohibited this balance from being saved:</h2>

      <ul>
      <% @balance.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :date %><br>
    <%= f.text_field :date %>
  </div>

  <div class="field">
    <%= f.label :account_id %><br>
    <%= f.collection_select(:account_id, Account.all.where(active: true).order('name ASC'), :id, :name,{})%>
  </div>

  <div class="field">
    <%= f.label :balance %><br>
    <%= f.text_field :balance %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Upvotes: 1

Views: 503

Answers (1)

jvillian
jvillian

Reputation: 20263

So, it seems to me there's two parts to your challenge.

  1. Creating a form with multiple balances, and
  2. Handling the multiple balances in your controller.

I'm not going to go into a lot of detail. Hopefully, this will point you in the right direction...

On your balances form, use form_tag instead of form_for which will allow you to represent multiple balances (instead of binding to a single balance instance). Create a balances partial (that includes the account name (as plain text, not a drop down), a balance input, and an account.id hidden field). Iterate on all active accounts, using the balances partial, to create the appropriate inputs. The date field will be outside the iterator so that you get your one date input. You can fiddle with all that so your balances all end up in one params hash.

In your controller, grab the params hash for balances, and iterate on that to create the balances using the one date value for each new balance.

I know that's very high level. And I apologize. But, writing out all the code is more than I have time for at the moment. Hope this helps you with the approach.

Upvotes: 1

Related Questions