Day 45 : #100DaysofCode - Basic Nested Forms
Tags: 100daysofcode, codenewbie
Published: 2020-07-16 02:36:30

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. 

<h3>So what is a Nested Form? </h3>

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](https://media.giphy.com/media/a0FuPjiLZev4c/giphy.gif)

Well great because mine did too! 

What does ``accepts_nested_attributes_for :ingredients`` mean?

According to the documentation, <a href="https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html"> nested attributes allow you to save attributes on associated records through the parent. </a>

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 <a href="https://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for"> 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. </a>

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`` <a href="https://apidock.com/rails/ActiveRecord/Associations/CollectionProxy/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. </a>

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<a href="https://api.rubyonrails.org/classes/ActionController/Parameters.html"> 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.</a>

This is the very basics of Nested forms and I hope this helps someone. 

As always, thanks for reading.

Sincerely,
Brittany