Skip to content

Commit 4096dce

Browse files
committed
It should be possible to specify multiple new has_many record links
1 parent ee49d3d commit 4096dce

4 files changed

Lines changed: 60 additions & 18 deletions

File tree

app/assets/javascripts/active_admin/lib/has_many.es6

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ $(function() {
4747
const regex = new RegExp($(this).data('placeholder'), 'g');
4848
const html = $(this).data('html').replace(regex, index);
4949

50-
const fieldset = $(html).insertBefore(this);
50+
const fieldset = $(html).insertBefore(parent.find('a.button.has_many_add').first());
5151
recompute_positions(parent);
5252
return parent.trigger('has_many_add:after', [fieldset, parent]);
5353
}

lib/active_admin/form_builder.rb

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ class HasManyBuilder < SimpleDelegator
4040
attr_reader :assoc
4141
attr_reader :options
4242
attr_reader :heading, :sortable_column, :sortable_start
43-
attr_reader :new_record, :destroy_option
43+
attr_reader :new_records, :destroy_option
44+
45+
NewRecord = Struct.new(:object, :text)
4446

4547
def initialize(has_many_form, assoc, options)
4648
super has_many_form
@@ -70,11 +72,32 @@ def extract_custom_settings!(options)
7072
@heading = options.key?(:heading) ? options.delete(:heading) : default_heading
7173
@sortable_column = options.delete(:sortable)
7274
@sortable_start = options.delete(:sortable_start) || 0
73-
@new_record = options.key?(:new_record) ? options.delete(:new_record) : true
75+
@new_records = extract_new_records(options)
7476
@destroy_option = options.delete(:allow_destroy)
7577
options
7678
end
7779

80+
def extract_new_records(options)
81+
Array.wrap(options.fetch(:new_record, {})).flat_map do |new_record|
82+
option =
83+
case new_record
84+
when false
85+
next []
86+
when Hash
87+
new_record
88+
when String
89+
{ text: new_record }
90+
else
91+
{ object: new_record }
92+
end
93+
object = option.fetch(:object, assoc_klass.new)
94+
[NewRecord.new(
95+
object,
96+
option.fetch(:text, I18n.t('active_admin.has_many_new', model: object.class.model_name.human))
97+
)]
98+
end
99+
end
100+
78101
def default_heading
79102
assoc_klass.model_name.
80103
human(count: ::ActiveAdmin::Helpers::I18n::PLURAL_MANY_COUNT)
@@ -93,7 +116,7 @@ def content_has_many(&block)
93116
contents = without_wrapper { inputs(options, &form_block) }
94117
contents ||= "".html_safe
95118

96-
js = new_record ? js_for_has_many(options[:class], &form_block) : ''
119+
js = js_for_has_many(options[:class], &form_block)
97120
contents << js
98121
end
99122

@@ -156,19 +179,20 @@ def without_wrapper
156179

157180
# Capture the ADD JS
158181
def js_for_has_many(class_string, &form_block)
159-
assoc_name = assoc_klass.model_name
160-
placeholder = "NEW_#{assoc_name.to_s.underscore.upcase.gsub(/\//, '_')}_RECORD"
161-
opts = {
162-
for: [assoc, assoc_klass.new],
163-
class: class_string,
164-
for_options: { child_index: placeholder }
165-
}
166-
html = template.capture{ __getobj__.send(:inputs_for_nested_attributes, opts, &form_block) }
167-
text = new_record.is_a?(String) ? new_record : I18n.t('active_admin.has_many_new', model: assoc_name.human)
168-
169-
template.link_to text, '#', class: "button has_many_add", data: {
170-
html: CGI.escapeHTML(html).html_safe, placeholder: placeholder
171-
}
182+
template.safe_join(new_records.map do |new_record|
183+
assoc_name = assoc_klass.model_name
184+
placeholder = "NEW_#{assoc_name.to_s.underscore.upcase.gsub(/\//, '_')}_RECORD"
185+
opts = {
186+
for: [assoc, new_record.object],
187+
class: class_string,
188+
for_options: { child_index: placeholder }
189+
}
190+
html = template.capture { __getobj__.send(:inputs_for_nested_attributes, opts, &form_block) }
191+
192+
template.link_to new_record.text, '#', class: "button has_many_add", data: {
193+
html: CGI.escapeHTML(html).html_safe, placeholder: placeholder
194+
}
195+
end)
172196
end
173197

174198
def wrap_div_or_li(html)

spec/support/rails_template.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
create_file 'app/assets/javascripts/some-random-js.js'
77
create_file 'app/assets/images/a/favicon.ico'
88

9-
generate :model, 'post title:string body:text published_date:date author_id:integer ' +
9+
generate :model, 'post type:string title:string body:text published_date:date author_id:integer ' +
1010
'position:integer custom_category_id:integer starred:boolean foo_id:integer'
1111
create_file 'app/models/post.rb', <<-RUBY.strip_heredoc, force: true
1212
class Post < ActiveRecord::Base
@@ -29,6 +29,8 @@ class Post < ActiveRecord::Base
2929
end
3030
3131
end
32+
class AnonymousPost < Post
33+
end
3234
RUBY
3335
copy_file File.expand_path('../templates/post_decorator.rb', __FILE__), 'app/models/post_decorator.rb'
3436

spec/unit/form_builder_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,22 @@ def user
625625
end
626626
end
627627

628+
describe "with multiple new record links" do
629+
let :body do
630+
build_form({url: '/categories'}, Category.new) do |f|
631+
f.object.posts.build
632+
f.has_many :posts, new_record: [Post.new, AnonymousPost.new] do |p|
633+
p.input :title
634+
p.input :author unless p.object.is_a?(AnonymousPost)
635+
end
636+
end
637+
end
638+
639+
it "should add a custom new record link" do
640+
expect(body).to have_selector("a", text: "Add New Post").and(have_selector("a", text: "Add New Anonymous post"))
641+
end
642+
end
643+
628644
describe "with allow destroy" do
629645
shared_examples_for "has many with allow_destroy = true" do |child_num|
630646
it "should render the nested form" do

0 commit comments

Comments
 (0)