I am writing this blog for myself more than for the readers, so bare with me. Today I am going to try and explain Basic Nested Forms and what I am learning.
So what is a Nested Form?
How I would describe a nested form, and I could be completely wrong so correct me if I am, a nested forms allows a user to create or update a database for multiple models at once. For example, lets pretend you want to create a recipe book that accepts ingredients and your schema looked like this:
ActiveRecord::Schema.define(version: 20160114135651) do create_table "ingredients", force: :cascade do |t| t.string "item_name" t.string "amount" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "recipe_id" end create_table "recipes", force: :cascade do |t| t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false end end
You would have to create an association to show that an ingredient belongs to a recipe and that a recipe can have many ingredients. You would do that by adding the following to your ingredient model:
class Ingredient < ActiveRecord::Base belongs_to :recipe end
and this piece of code to your recipe model:
class Recipe < ActiveRecord::Base has_many :ingredients accepts_nested_attributes_for :ingredients end
Now when you look at the code above, did your eyebrow raise?
Well great because mine did too!
accepts_nested_attributes_for :ingredients mean?
According to the documentation, nested attributes allow you to save attributes on associated records through the parent.
As usual, I wish people who wrote these docs wrote in English,
accepts_nested_attributes_for :ingredients allows us to reference the attributes for ingredients in our recipe model/forms.
So, how do we reference the ingredients attributes in the form? Well, like this:
<%= form_for @recipe do |f| %> <%= f.label :name %> <%= f.text_field :name%><br> <%= f.fields_for :ingredients do |i| %> <%= i.label :item_name %> <%= i.text_field :item_name %><br> <%= i.label :amount %> <%= i.text_field :amount %><br> <% end %> <%= f.submit %> <% end %>
The fields_for creates a scope around a specific model object like form_for, but doesn’t create the form tags themselves. This makes fields_for suitable for specifying additional model objects in the same form.
So, it allows you to reference the ingredients in the recipe form, GREAT! However, in the current state there will be nothing in the fields, we must first create an instance of recipe and ingredients. This brings us to the final step, we must update the recipe controller:
def new @recipe = Recipe.new @recipe.ingredients.build(name: "Ingredient 1") @recipe.ingredients.build(name: "Ingredient 2") end def create recipe = Recipe.create(recipe_params) redirect_to recipes_path end private def recipe_params params.require(:recipe).permit( :title, ingredients_attributes: [ :name, :quantity ] ) end
new method creates a new recipe instance to be referenced in the form and the
.build returns a new object of the collection type that has been instantiated with attributes and linked to this object, but have not yet been saved. You can pass an array of attributes hashes, this will return an array with the new objects.
So now, the form will know what to reference by using that new instance of recipe and ingredients created.
create method creates a recipe and that accepts the
recipe_params method allows us to choose the ingredients and recipe attributes that should be permitted for updating and allows us to prevent accidentally exposing that which shouldn't be exposed.
This is the very basics of Nested forms and I hope this helps someone.
As always, thanks for reading.