Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/assets/javascripts/active_admin/lib/has_many.es6
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ $(function() {
const regex = new RegExp($(this).data('placeholder'), 'g');
const html = $(this).data('html').replace(regex, index);

const fieldset = $(html).insertBefore(this);
const fieldset = $(html).insertBefore(parent.find('a.button.has_many_add').first());
recompute_positions(parent);
return parent.trigger('has_many_add:after', [fieldset, parent]);
}
Expand Down
56 changes: 40 additions & 16 deletions lib/active_admin/form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ class HasManyBuilder < SimpleDelegator
attr_reader :assoc
attr_reader :options
attr_reader :heading, :sortable_column, :sortable_start
attr_reader :new_record, :destroy_option
attr_reader :new_records, :destroy_option

NewRecord = Struct.new(:object, :text)

def initialize(has_many_form, assoc, options)
super has_many_form
Expand Down Expand Up @@ -70,11 +72,32 @@ def extract_custom_settings!(options)
@heading = options.key?(:heading) ? options.delete(:heading) : default_heading
@sortable_column = options.delete(:sortable)
@sortable_start = options.delete(:sortable_start) || 0
@new_record = options.key?(:new_record) ? options.delete(:new_record) : true
@new_records = extract_new_records(options)
@destroy_option = options.delete(:allow_destroy)
options
end

def extract_new_records(options)
Array.wrap(options.fetch(:new_record, {})).flat_map do |new_record|
option =
case new_record
when false
next []
when Hash
new_record
when String
{ text: new_record }
else
{ object: new_record }
end
object = option.fetch(:object, assoc_klass.new)
[NewRecord.new(
object,
option.fetch(:text, I18n.t('active_admin.has_many_new', model: object.class.model_name.human))
)]
end
end

def default_heading
assoc_klass.model_name.
human(count: ::ActiveAdmin::Helpers::I18n::PLURAL_MANY_COUNT)
Expand All @@ -93,7 +116,7 @@ def content_has_many(&block)
contents = without_wrapper { inputs(options, &form_block) }
contents ||= "".html_safe

js = new_record ? js_for_has_many(options[:class], &form_block) : ''
js = js_for_has_many(options[:class], &form_block)
contents << js
end

Expand Down Expand Up @@ -156,19 +179,20 @@ def without_wrapper

# Capture the ADD JS
def js_for_has_many(class_string, &form_block)
assoc_name = assoc_klass.model_name
placeholder = "NEW_#{assoc_name.to_s.underscore.upcase.gsub(/\//, '_')}_RECORD"
opts = {
for: [assoc, assoc_klass.new],
class: class_string,
for_options: { child_index: placeholder }
}
html = template.capture{ __getobj__.send(:inputs_for_nested_attributes, opts, &form_block) }
text = new_record.is_a?(String) ? new_record : I18n.t('active_admin.has_many_new', model: assoc_name.human)

template.link_to text, '#', class: "button has_many_add", data: {
html: CGI.escapeHTML(html).html_safe, placeholder: placeholder
}
template.safe_join(new_records.map do |new_record|
assoc_name = assoc_klass.model_name
placeholder = "NEW_#{assoc_name.to_s.underscore.upcase.gsub(/\//, '_')}_RECORD"
opts = {
for: [assoc, new_record.object],
class: class_string,
for_options: { child_index: placeholder }
}
html = template.capture { __getobj__.send(:inputs_for_nested_attributes, opts, &form_block) }

template.link_to new_record.text, '#', class: "button has_many_add", data: {
html: CGI.escapeHTML(html).html_safe, placeholder: placeholder
}
end)
end

def wrap_div_or_li(html)
Expand Down
4 changes: 3 additions & 1 deletion spec/support/rails_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
create_file 'app/assets/javascripts/some-random-js.js'
create_file 'app/assets/images/a/favicon.ico'

generate :model, 'post title:string body:text published_date:date author_id:integer ' +
generate :model, 'post type:string title:string body:text published_date:date author_id:integer ' +
'position:integer custom_category_id:integer starred:boolean foo_id:integer'
create_file 'app/models/post.rb', <<-RUBY.strip_heredoc, force: true
class Post < ActiveRecord::Base
Expand All @@ -29,6 +29,8 @@ class Post < ActiveRecord::Base
end

end
class AnonymousPost < Post
end
RUBY
copy_file File.expand_path('../templates/post_decorator.rb', __FILE__), 'app/models/post_decorator.rb'

Expand Down
16 changes: 16 additions & 0 deletions spec/unit/form_builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,22 @@ def user
end
end

describe "with multiple new record links" do
let :body do
build_form({url: '/categories'}, Category.new) do |f|
f.object.posts.build
f.has_many :posts, new_record: [Post.new, AnonymousPost.new] do |p|
p.input :title
p.input :author unless p.object.is_a?(AnonymousPost)
end
end
end

it "should add a custom new record link" do
expect(body).to have_selector("a", text: "Add New Post").and(have_selector("a", text: "Add New Anonymous post"))
end
end

describe "with allow destroy" do
shared_examples_for "has many with allow_destroy = true" do |child_num|
it "should render the nested form" do
Expand Down