Reputation: 889
I have a rails app that has building and floors models
class Building < ApplicationRecord
has_many :floors
end
class Floor < ApplicationRecord
belongs_to :building
end
In my building form I want to ask the user how many floors the building has, and then when the building is created I want to add that many floors.
So the form would look like so:
<%= form_with(model: building, local: true) do |form| %>
<% if building.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(building.errors.count, "error") %> prohibited this building from being saved:</h2>
<ul>
<% building.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div class="field">
<%= form.label :" How many floors does the building have" %>
<%= form.number :floors %> * not sure how to do this
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
Note I don't want the number of floors saved on the building model, it just creates the number of floors the user specified.
So if I created a building called "Walts Place" and said it has 10 floors it would create: Walts Place with id:1, and 10 floors with the building_id of 1.
Does that make sense?
Your help is greatly appreciated.
Update:
ActiveRecord::Schema.define(version: 2019_07_30_093037) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "buildings", force: :cascade do |t|
t.string "name"
t.float "distancetocondensors"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "floors", force: :cascade do |t|
t.bigint "building_id", null: false
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["building_id"], name: "index_floors_on_building_id"
end
add_foreign_key "floors", "buildings"
end
Upvotes: 2
Views: 309
Reputation: 1482
you can modify it in your building controller in create action.
def create
@building = Building.new(building_params)
if @building.save
floors = params[:number].to_i
floors.times do
Floor.create(building: @building)
end
redirect_to building_path
else
redirect_to error
end
end
in your form add a field for number of floor without erb
<%= form_with(model: building, local: true) do |form| %>
<% if building.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(building.errors.count, "error") %> prohibited this building
from being saved:</h2>
<ul>
<% building.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div class="field">
<label> How many floors does the building have</label>
<input type="number" name="number"/>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
Upvotes: 1
Reputation: 10237
You can use nested attributes
to help you here.
First, in your Building
model add:
has_many :floors
accepts_nested_attributes_for :floors
Then in your BuildingController
's create
action do:
class BuildingController < ApplicationController
...
def create
floors_amount = params[:building][:floors] || 0
building_params = params.require(:building).permit(:name).merge({
# just fill floors array with empty hashes since `id` will be added automatically
floors_attributes: (1..floors_amount).to_a.fill({})
})
@building = Building.create(building_params)
if @buildnig.errors
render :new # show the form page with an error
else
redirect_to @building # or whenever you want to redirect
end
end
...
end
The advantage here is that it wraps the creation of building and floors into a transaction so if something goes wrong it will rollback all changes. I.e. there won't be a case where new Building is inserted into DB but floors didn't due to some error during their creation.
Another convenience here is that if some validation error appears either on building
or on any of the floor
then it will be set into building.errors
. Means you can easily do render :new
in the case of errors to display them.
I see that in your form view you access Building instance as building
. So I'm not really sure how you pass it to this view.
In my example I saved building into @building
variable so that in this view form you will have to access it as @building
. I believe in your new
action in BuildingController
you should set it to @building
as well:
class BuildingController < ApplicationController
...
def new
@building = Building.new
end
...
end
And in your view you will access it as @building
then, not building
.
Hope, that makes sense
Upvotes: 0