NestedFormReflex
A reflex to construct a form that wraps a has_many
relationship with nested attributes on the fly.
How?
- New children are instantiated by calling
.build
on thehas_many
association fields_for
expands to all children if achild_attributes=
setter is present (which is the case ifaccepts_nested_attributes_for
is set) - see API docs
Caveat
Clean up your session (or other persistent store) after form submission.
app/reflexes/nested_form_reflex.rb
class NestedFormReflex < ApplicationReflex
before_reflex do
session[:forms] ||= {}
session[:forms]["Book"] ||= Book.new
end
def add
session[:forms]["Book"].chapters.build
end
end
app/controllers/books_controller.rb
class BooksController < ApplicationController
def new
@book = session.fetch(:forms, {}).fetch("Book", Book.new)
end
end
app/models/book.rb
class Book < ApplicationRecord
has_many :chapters
accepts_nested_attributes_for :chapters
end
app/models/chapter.rb
class Chapter < ApplicationRecord
end
app/views/books/new.htm.erb
<%= form_for(@book, url: "#") do |form| %>
<div class="mb-3 row">
<%= form.label :title, class: "col-sm-2 col-form-label" %>
<div class="col-sm-10">
<%= form.text_field :title, class: "form-control", placeholder: "Enter a book title" %>
</div>
</div>
<hr/>
<%= form.fields_for :chapters do |chapter_fields| %>
<h4>Chapter <%= chapter_fields.index + 1 %></h4>
<div class="mb-3 row">
<%= chapter_fields.label :title, class: "col-sm-2 col-form-label" %>
<div class="col-sm-10">
<%= chapter_fields.text_field :title, class: "form-control", placeholder: "Enter a chapter title", data: {reflex_permanent: true} %>
</div>
</div>
<% end %>
<button type="button" class="btn btn-outline-primary" data-reflex="click->NestedForm#add">Add Chapter</button>
<% end %>