Skip to content

Instantly share code, notes, and snippets.

@fran-worley
Last active October 23, 2017 23:42
Show Gist options
  • Save fran-worley/d47287d86b535fbfc71f to your computer and use it in GitHub Desktop.
Save fran-worley/d47287d86b535fbfc71f to your computer and use it in GitHub Desktop.
A working example of nested forms just using Reform.
#views/users/_form.html.erb
<%= simple_form_for @form do |f| %>
<%= f.error_notification %>
<%= f.input :name %>
<%= f.input :email %>
<h4>
<%= link_to_add_fields icon(:plus),
f,
:assignments,
class: "btn btn-success",
append_selector: "#user_assignments tbody tr:first",
new_object: AssignmentForm.new(Assignment.new) %>
Assignments
</h4>
<table class="table table-hover table-bordered" id="user_assignments">
<thead>
<tr>
<th>Assignment</th>
<th></th>
</tr>
</thead>
<tbody>
<%= f.simple_fields_for :assignments do |builder| %>
<tr>
<td>
<%= f.input :assignment_id,
label: false,
collection: @assignments,
prompt: "Select a assignment" %>
</td>
<td>
<%= f.hidden_field :_destroy, class: "destroy-input" %><!--Set via javascript on click of remove button -->
<%= link_to "Remove", '#', class: "remove_fields btn btn-xs btn-danger" %></td>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= f.button :submit %> <%= link_to "Cancel", :back, class: 'btn btn-warning' %>
<% end %>
#models/assignment.rb
class Assignment < ActiveRecord::Base
belongs_to :user
end #class Assignment
#forms/assignment_form.rb
class AssignmentForm < Reform::Form
property :id, type: Integer, writeable: false
property :_destroy, virtual: true
property :assignment_id, type: Integer
validation :default do
key(:assignments_id) { |assignment_id| assignment_id.filled? & assignment_id.int? }
end
end #class AssignmentForm
#forms/reform/nested_form.rb
module Reform
module NestedForm
extend ActiveSupport::Concern #might not be needed... Infact probably isn't
def nested_removeable!(collection:, fragment:, binding:, **)
deserialized_item = collection.find { |item| item.id.to_s == fragment["id"] } if fragment["id"].present?
if fragment["_destroy"] == "true" # don't process if it's getting removed!
collection.delete(deserialized_item) if deserialized_item.present?
return skip!
else
return deserialized_item || collection.append(model.send(binding.name).new)
end
end
end
end
#assets/javascripts/nested_forms.js.coffee
#add fields to form. used by nested forms
$.fn.addFields = (id, fields) ->
time = new Date().getTime()
id = new RegExp(id, "g")
fields = $(fields.replace(id, time))
this.before(fields)
#remove fields from form. used by nested forms
$.fn.removeFields = (destroyInputSelector = ".destroy-input") ->
this.find(destroyInputSelector).val('true')
this.hide()
this
jQuery ->
#enables add and remove fields from nested forms
$('form').on 'click', '.remove_fields', (event) ->
$($(this).attr("data-fields-selector")).removeFields()
event.preventDefault()
$('form').on 'click', '.add_fields', (event) ->
$($(this).data("append-selector")).addFields($(this).data('id'), $(this).data('fields'))
event.preventDefault()
#models/user.rb
class User < ActiveRecord::Base
has_many :assignments
end #class User
#forms/user_form.rb
class UserForm < Reform::Form
include Reform::NestedForm
property :name
property :email
collection :assignments, populator: :nested_removeable!, skip_if: :all_blank, form: AssignmentForm
def prepopulate!(options={})
return if model.client.blank?
self.assignments << model.assignments.new if assignments.size == 0
end
end #class UserForm
@Mbuckley0
Copy link

You are using a link_to_add_fields helper method in your view but this is not defined anywhere in this gist. Where does this come from?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment