Created
May 17, 2017 22:34
-
-
Save mdchaney/8d8de28b6776ca3d14630e8771267a3c to your computer and use it in GitHub Desktop.
Help for dynamic subforms in Rails
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
In this file, the fields can be set to "required", but that will | |
cause issues. Specifically, when someone clicks "Remove" the fields | |
will be hidden and unless the "required" prop is set to false the form | |
will not submit. Also note that when showing this line if the object | |
is marked_for_destruction? it should be hidden. The javascript below | |
will handle both these issues. | |
--> | |
<tr class="line_item subform fields" id="line_item_<%= f.object.id %>"> | |
<td> | |
<%= f.text_field :field_1 %> | |
</td> | |
<td> | |
<%= f.text_field :field_2 %> | |
</td> | |
<td> | |
<%= remove_child_link 'Remove', f %> | |
</td> | |
</tr> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
Note that this belongs in a form where "f" is the form variable. | |
In this case, we are dealing with a child model called LineItem. | |
Make careful note of the pluralization of "line_item" below - some | |
must be plural and others singular. See the _line_item.html.erb | |
file for an example of the actual subform. Also note that you | |
must allow for "id" and "_destroy" fields in your strong parameters | |
for line_items_attributes. | |
--> | |
<fieldset> | |
<legend>Line Items</legend> | |
<table id='line_items_tbl'> | |
<thead> | |
<tr><th>Field 1</th><th>Field 2</th><th>Actions</th></tr> | |
</thead> | |
<tbody id="line_items"> | |
<%= f.fields_for :line_items do |li_form|%> | |
<%= render partial: "line_item", locals: { f: li_form } %> | |
<% end %> | |
</tbody> | |
</table> | |
<%= add_child_button 'Add A Line Item', f, :line_items, partial: 'line_item' %> | |
</fieldset> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module ApplicationHelper | |
def remove_child_link(name, f, options = {}) | |
confirm = options.delete(:confirm) | |
confirm = confirm.nil? ? true : confirm | |
f.hidden_field(:_destroy) + link_to(name, '#', onclick: "if (#{confirm ? 'false' : 'true'} || confirm('Do you really want to remove this item?')) remove_fields(this); return false;") | |
end | |
def add_child_link(name, f, method, options = {}) | |
fields = new_child_fields(f, method, options) | |
link_to(name, '#', onclick: "insert_fields(this, \"#{method}\", \"#{escape_javascript(fields)}\"); return false;") | |
end | |
def new_child_fields(form_builder, method, options = {}) | |
options[:object] ||= form_builder.object.class.reflect_on_association(method).klass.new | |
options[:partial] ||= method.to_s.singularize | |
options[:form_builder_local] ||= :f | |
form_builder.fields_for(method, options[:object], :child_index => "new_#{method}") do |f| | |
render(:partial => options[:partial], :locals => { options[:form_builder_local] => f }) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Requires jquery | |
// helpers for nested forms | |
function insert_fields(link, method, content) { | |
var new_id = new Date().getTime(); | |
var regexp = new RegExp("new_" + method, "g") | |
if (content.match(/^\s*<tr\b/)) { | |
// Add it to a table | |
$(content.replace(regexp,new_id)).appendTo($(link).parents('fieldset').children('table:last')); | |
} else if (content.match(/^\s*<li\b/)) { | |
// Add it to a ul | |
$(content.replace(regexp,new_id)).appendTo($(link).parents('fieldset').children('ul:last')); | |
} else { | |
$(content.replace(regexp,new_id)).insertAfter($(link).parents('fieldset').children(':last')); | |
} | |
} | |
function remove_fields(link) { | |
$('input[type=hidden][name*="[_destroy]"]', $(link).parent()).val('true'); | |
if ($(link).parents('.fields').size()>0) { | |
$(link).parents('.fields').first().hide(); | |
$('input, select, textarea',$(link).parents('.fields').first()).prop('required',false); | |
} | |
} | |
function hide_removed_records() { | |
$('input[type=hidden][value=true][name$="[_destroy]"], input[type=checkbox][checked][value=1][name$="[_destroy]"]').each(function() { | |
if ($(this).parents('.fields').size()>0) { | |
$(this).parents('.fields').first().hide(); | |
$('input',$(link).parents('.fields').first()).prop('required',false); | |
} | |
}); | |
} | |
$(document).ready(function() { | |
hide_removed_records(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This code is used to handle add/remove for child records in a Ruby on Rails form. As an example, I'm using a child model called LineItem. Each file here is necessary. You must modify the form and subform to suit your particular data model, and include the relevant code in your ApplicationHelper as well as in your javascript directory. This code absolutely relies on jQuery.