Skip to content
2 changes: 1 addition & 1 deletion README.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Use this form just like normal, including the fields_for helper method for nesti
<%= task_form.text_field :name %>
<%= task_form.link_to_remove "Remove this task" %>
<% end %>
<%= f.link_to_add "Add a task", :tasks %>
<p><%= f.link_to_add "Add a task", :tasks %></p>

This generates links which dynamically add and remove fields.

Expand Down
9 changes: 3 additions & 6 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@ Bundler::GemHelper.install_tasks

require 'rubygems'
require 'rake'
require 'spec/rake/spectask'

spec_files = Rake::FileList["spec/**/*_spec.rb"]
require 'rspec/core/rake_task'

desc "Run specs"
Spec::Rake::SpecTask.new do |t|
t.spec_files = spec_files
t.spec_opts = ["-c"]
RSpec::Core::RakeTask.new(:spec) do |t|
t.rspec_opts = ["-c"]
end

task :default => :spec
9 changes: 6 additions & 3 deletions lib/generators/nested_form/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ def self.source_root
File.dirname(__FILE__) + "/templates"
end


def copy_jquery_file
copy_file 'nested_form.js', 'public/javascripts/nested_form.js'
if File.exists?('public/javascripts/prototype.js')
copy_file 'prototype_nested_form.js', 'public/javascripts/nested_form.js'
else
copy_file 'jquery_nested_form.js', 'public/javascripts/nested_form.js'
end
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ $('form a.add_nested_fields').live('click', function() {

// Make the context correct by replacing new_<parents> with the generated ID
// of each of the parent objects
var context = ($(this).closest('.fields').find('input:first').attr('name') || '').replace(new RegExp('\[[a-z]+\]$'), '');
var context = ($(this).closest('.fields').find('input:first').attr('name') || '').replace(new RegExp('\[[a-z_]+\]$'), '');

// context will be something like this for a brand new form:
// project[tasks_attributes][1255929127459][assignments_attributes][1255929128105]
Expand All @@ -15,15 +15,17 @@ $('form a.add_nested_fields').live('click', function() {
if(context) {
var parent_names = context.match(/[a-z_]+_attributes/g) || [];
var parent_ids = context.match(/[0-9]+/g);

for(i = 0; i < parent_names.length; i++) {
if(parent_ids[i]) {
content = content.replace(
new RegExp('(\\[' + parent_names[i] + '\\])\\[.+?\\]', 'g'),
'$1[' + parent_ids[i] + ']'
)
}
}

if(parent_ids) {
for(i = 0; i < parent_names.length; i++) {
if(parent_ids[i]) {
content = content.replace(
new RegExp('(\\[' + parent_names[i] + '\\])\\[.+?\\]', 'g'),
'$1[' + parent_ids[i] + ']'
)
}
}
}
}

// Make a unique ID for the new child
Expand Down
50 changes: 50 additions & 0 deletions lib/generators/nested_form/templates/prototype_nested_form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
document.observe('click', function(e, el) {
if (el = e.findElement('form a.add_nested_fields')) {
// Setup
var assoc = el.readAttribute('data-association'); // Name of child
var content = $(assoc + '_fields_blueprint').innerHTML; // Fields template

// Make the context correct by replacing new_<parents> with the generated ID
// of each of the parent objects
var context = (el.getOffsetParent('.fields').firstDescendant().readAttribute('name') || '').replace(new RegExp('\[[a-z_]+\]$'), '');

// context will be something like this for a brand new form:
// project[tasks_attributes][1255929127459][assignments_attributes][1255929128105]
// or for an edit form:
// project[tasks_attributes][0][assignments_attributes][1]
if(context) {
var parent_names = context.match(/[a-z_]+_attributes/g) || [];
var parent_ids = context.match(/[0-9]+/g);

if(parent_ids) {
for(i = 0; i < parent_names.length; i++) {
if(parent_ids[i]) {
content = content.replace(
new RegExp('(\\[' + parent_names[i] + '\\])\\[.+?\\]', 'g'),
'$1[' + parent_ids[i] + ']'
)
}
}
}
}

// Make a unique ID for the new child
var regexp = new RegExp('new_' + assoc, 'g');
var new_id = new Date().getTime();
content = content.replace(regexp, new_id);

el.insert({ before: content });
return false;
}
});

document.observe('click', function(e, el) {
if (el = e.findElement('form a.remove_nested_fields')) {
var hidden_field = el.previous(0);
if(hidden_field) {
hidden_field.value = '1';
}
el.ancestors()[0].hide();
return false;
}
});
14 changes: 11 additions & 3 deletions lib/nested_form/builder.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module NestedForm
class Builder < ActionView::Helpers::FormBuilder
def link_to_add(name, association)
def link_to_add(name, association, html_options = {})
@fields ||= {}
@template.after_nested_form(association) do
model_object = object.class.reflect_on_association(association).klass.new
Expand All @@ -9,7 +9,15 @@ def link_to_add(name, association)
output.safe_concat('</div>')
output
end
@template.link_to(name, "javascript:void(0)", :class => "add_nested_fields", "data-association" => association)
#@template.link_to(name, "javascript:void(0)", :class => "add_nested_fields", "data-association" => association, html_options.merge!{html_options})
linkclass = "add_nested_fields"
if html_options.has_key?(:class)
html_options[:class] << " #{linkclass}"
else
html_options.merge!({:class => linkclass})
end
html_options.merge!({"data-association" => association})
@template.link_to(name, "javascript:void(0)", html_options)
end

def link_to_remove(name)
Expand All @@ -31,4 +39,4 @@ def fields_for_nested_model(name, association, args, block)
output
end
end
end
end
3 changes: 2 additions & 1 deletion spec/nested_form/view_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
@template.after_nested_form(:tasks) { @template.concat("123") }
@template.after_nested_form(:milestones) { @template.concat("456") }
@template.nested_form_for(Project.new) {}
@template.output_buffer.should include("123456")
@template.output_buffer.should include("123")
@template.output_buffer.should include("456")
end
end