Notes to Myself (Rails:Entry#2a - Nested Forms)
Published: 2021-11-06 22:00:00

This is a step-by-step instructional doc to use when creating nested forms.   It will cover belongs_to, has_many, and has_many_through associations.

The example models I will be using are Author, Posts, and Categories.

```ruby
class Author < ApplicationRecord
  has_many :posts
  has_many :categories, through: :posts
end
```
```ruby
class Post < ApplicationRecord
  belongs_to :category
  belongs_to :author
end
```
```ruby
class Category < ApplicationRecord
  has_many :posts
  has_many :authors, through: :posts
end
```

<h3>BELONGS_TO</h3>

The first scenario is when you have a child instance that has a belongs_to relationship with a parent class (in this example the child is the Post class which has 2 parent classes:  Author & Category), and we would like to create a new instance of the child object that is automatically associated with it's parent class.

There are 3 methods that will achieve this form.  The first assumes that a parent class already exists.  The second assumes that it does not exist.  The third assumes that there are potential parent classes which exist, but the user may want to create their own.
<br>

<h6>The Parent Class Exists</h6>

1) In the child controller, create a 'dummy' instance to trigger the form_for builder.  This will be the same for all three scenarios.

```ruby
  def new
    @post = Post.new
  end
```
2) On the child form page, use text_field for the child attributes and collection_select for the parent attributes. 

```ruby
<%= form_for @post do |f| %>
<div><%= f.label "Post Content" %>
     <%= f.text_field :content %></div>
<div><%= f.label "Author" %>
     <%= f.collection_select author_id, Author.all, :id, :name %></div>
<div><%= f.label "Category" %>
     <%= f.collection_select category_id, Category.all, :id, :name %></div>
     <%= f.submit %>
<% end %> 
```
3) In the child controller, define strong params.

```ruby
  private

  def post_params
    params.require(:post).permit(
      :content, 
      :category_id, 
      :author_id
    )
  end
```
4) In the child controller, create the child object.  This will also be the same for all three scenarios.

```ruby
 def create
    Post.create(post_params)
    redirect_to posts_path
  end
```

_*special note - Author.all and Category.all (used on the form page) can & should be refactored in view helper_
<br>

<h6>The Parent Class Does Not Exist</h6>

1) see Ex.1

2) In the child model, define a setter/getter method for the parent. This will be the same in the 3rd scenario.

```ruby
def author_name=(name)
  self.author = Author.find_or_create_by(name: name)
end

def author_name
   self.author ? self.author.name : nil
end

def category_name=(name)
  self.category = Category.find_or_create_by(name: name)
end

def category_name
   self.category ? self.category.name : nil
end
```
3) On the child form page, use text_field for all attributes of both child and parent.

```ruby
<%= form_for @post do |f| %>
<div><%= f.label "Post Content" %>
     <%= f.text_field :content %></div>
<div><%= f.label "Author" %>
     <%= f.text_field :author_name %></div>
<div><%= f.label "Category" %>
     <%= f.text_field :category_name %></div>
     <%= f.submit %>
<% end %> 
```
4) In the child controller, define strong params. This will be the same for the 3rd scenario.

```ruby
  private

  def post_params
    params.require(:post).permit(
      :content, 
      :category_name, 
      :author_name
    )
  end
```
5) see Ex.1

<br>

<h6>A Parent Class Exists, But You Want The Option To Create As Well</h6>

1) see Ex.1

2) see Ex.2

3) On the child form page, use text_field for the child attributes, and datalist for the parent.

```
<%= form_for @post do |f| %>
<div><%= f.label "Post Content" %>
     <%= f.text_field :content %></div>

<div><%= f.label "Author" %>
     <%= f.text_field :author_name, list: "authors_autocomplete" %>
          <datalist id="authors_autocomplete">
               <% Author.all.each do |author| %>
                 <option value="<%= author.name %>">
               <% end %>
          </datalist></div>

<div><%= f.label "Category" %>
     <%= f.text_field :category_name, list: "categories_autocomplete" %>
          <datalist id="categories_autocomplete">
               <% Category.all.each do |category| %>
                 <option value="<%= category.name %>">
               <% end %>
          </datalist></div>

     <%= f.submit %>
<% end %> 
```
4) see Ex.2

5) see Ex.1