loading...

Day 45 : #100DaysofCode - Basic Nested Forms

sincerelybrittany profile image Brittany ・3 min read

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?

Alt text of image

Well great because mine did too!

What does 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

The 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.

The create method creates a recipe and that accepts the recipe_params method.

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.

Sincerely,
Brittany

Posted on by:

sincerelybrittany profile

Brittany

@sincerelybrittany

Developer | Software Engineer πŸ‘©πŸΎβ€πŸ’» | Determined | Music & Dance | Completed #100DaysofCode | #WomenWhoCode

Discussion

markdown guide