While reading about creating forms in Rails, I came across Nested Forms. On the first glance it looked difficult, but as I progressed, I said to myself, “It’s not so bad!”.
In order to create Nested Forms, it is important to understand about the helpers provided by Rails.
1). accepts_nested_attributes_for
: Imagine that we have a BookShelf which has many Books. And the Books contain a title and an author. This setup calls for nested attributes.
class BookShelf < ActiveRecord::Base
has_many :books
accepts_nested_attributes_for :books
end
accepts_nested_attributes_for
provides us with books_attributes=
(setter) method. Now, we can set the attributes of model books
, through bookshelf
hash. The params in this case will be.
params = {
bookshelf: {
size: "small",
books_attributes: [
{title: "Coders at Work", author: "Peter Seibel"},
{title: "Design Patterns in Ruby", author: "Russ Olsen"},
{title: "The Pragmatic Programmer", author: "Andy Hunt"}
]
}
}
accepts_nested_attributes_for
also provides us with some options:
-
reject_if
: Helps us to ignore the hashes that fail the criteria or required validations.class BookShelf < ActiveRecord::Base has_many :books accepts_nested_attributes_for :books, reject_if: proc { |attribute| attribute['title'].blank? } end
-
allow_destroy
: Allows us to delete the records trough attributes hash.class BookShelf < ActiveRecord::Base has_many :books accepts_nested_attributes_for :books, allow_destroy: true end
2). fields_for
: This helper takes two arguments. The first argument is the model for which we want to create the attributes and the second is the object. It allows the methods to be called on builder in order to generate fields.
<%= f.fields_for :books, Book.new do |book_attributes| %>
<%= book_attributes.text_field :title %>
<%= book_attributes.text_field :author %>
<% end %>
Using these form helpers, we can create nested forms.
Step1. Add accepts_nested_attributes_for
in the model whose hash will contain the nested attributes.
class BookShelf < ActiveRecord::Base
has_many :books
accepts_nested_attributes_for :books
end
As stated above, this code will provide us with books_attributes=
method.
Step2. Update strong parameters in BookShelf
controller so that it accepts the books_attributes
.
class BookShelvesController < ApplicationController
def index
@bookshelves = BookShelf.all
end
def show
@bookshelf = BookShelf.find(params[:id])
end
def new
@bookshelf = BookShelf.new
end
def create
@bookshelf = BookShelf.new(bookshelf_params)
if @bookshelf.save
redirect_to bookshelf_path(@bookshelf)
else
render new_bookshelf_path
end
end
private
def bookshelf_params
params.require(:bookshelf).permit(:size, books_attributes: [:title, :author])
end
end
Step 3. Updating the view. In our view, we use fields_for
helper method.
<div>
<%= form_for :bookshelf do |f| %>
<div>
<%= f.label :size %>
<%= f.text_field :size %>
<div/>
<div>
<%= f.fields_for :books, Book.new do |book_attributes| %>
<%= book_attributes.label :title %>
<%= book_attributes.text_field :title %>
<br/>
<%= book_attributes.label :author %>
<%= book_attributes.text_field :author %>
<% end %>
<div/>
<%= f.submit %>
<% end %>
<div/>
Finally, login to rails server and check the form.