Skip to content

Commit 876be11

Browse files
committed
Introduce SingularAssociation and CollectionAssociation to tidy up the views
1 parent d20b024 commit 876be11

18 files changed

+262
-177
lines changed

app/helpers/rails_admin/application_helper.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ def authorized?(action_name, abstract_model = nil, object = nil)
77
action(action_name, abstract_model, object).try(:authorized?)
88
end
99

10+
def current_action
11+
params[:action].in?(%w[create new]) ? 'create' : 'update'
12+
end
13+
1014
def current_action?(action, abstract_model = @abstract_model, object = @object)
1115
@action.custom_key == action.custom_key &&
1216
abstract_model.try(:to_param) == @abstract_model.try(:to_param) &&

app/views/rails_admin/main/_form_filtering_multiselect.html.erb

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,14 @@
11
<%
22
config = field.associated_model_config
3-
source_abstract_model = RailsAdmin.config(form.object.class).abstract_model
4-
5-
selected = form.object.send(field.name)
6-
selected_ids = selected.map{|s| s.send(field.associated_primary_key).to_s}
7-
8-
current_action = params[:action].in?(['create', 'new']) ? 'create' : 'update'
9-
10-
xhr = !field.associated_collection_cache_all
11-
12-
collection = if xhr
13-
selected.map { |o| [o.send(field.associated_object_label_method), o.send(field.associated_primary_key)] }
14-
else
15-
i = 0
16-
controller.list_entries(config, :index, field.associated_collection_scope, false).map { |o| [o.send(field.associated_object_label_method), o.send(field.associated_primary_key).to_s] }.sort_by {|a| [selected_ids.index(a[1]) || selected_ids.size, i+=1] }
17-
end
18-
19-
js_data = {
20-
xhr: xhr,
21-
:'edit-url' => (field.inline_edit && authorized?(:edit, config.abstract_model) ? edit_path(model_name: config.abstract_model.to_param, id: '__ID__') : ''),
22-
remote_source: index_path(config.abstract_model, source_object_id: form.object.id, source_abstract_model: source_abstract_model.to_param, associated_collection: field.name, current_action: current_action, compact: true),
23-
scopeBy: field.dynamic_scope_relationships,
24-
sortable: !!field.orderable,
25-
removable: !!field.removable,
26-
cacheAll: !!field.associated_collection_cache_all,
27-
regional: {
28-
add: t('admin.misc.add_new'),
29-
chooseAll: t('admin.misc.chose_all'),
30-
clearAll: t('admin.misc.clear_all'),
31-
down: t('admin.misc.down'),
32-
remove: t('admin.misc.remove'),
33-
search: t('admin.misc.search'),
34-
up: t('admin.misc.up')
35-
}
36-
}
373
%>
4+
385
<div class="row">
396
<div class="col-auto">
407
<input name="<%= form.dom_name(field) %>" type="hidden" />
41-
<% selected_ids = (hdv = field.form_default_value).nil? ? selected_ids : hdv %>
42-
<%= form.select field.method_name, collection, { selected: selected_ids, object: form.object }, field.html_attributes.reverse_merge({data: { filteringmultiselect: true, options: js_data.to_json }, multiple: true}) %>
8+
<%=
9+
form.select field.method_name, field.collection, { selected: field.form_value, object: form.object },
10+
field.html_attributes.reverse_merge({data: { filteringmultiselect: true, options: field.widget_options.to_json }, multiple: true})
11+
%>
4312
</div>
4413
<% if authorized?(:new, config.abstract_model) && field.inline_add %>
4514
<div class="col-sm-4 modal-actions">
Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,21 @@
11
<%
22
config = field.associated_model_config
3-
source_abstract_model = RailsAdmin.config(form.object.class).abstract_model
4-
5-
current_action = params[:action].in?(['create', 'new']) ? 'create' : 'update'
6-
7-
edit_url = authorized?(:edit, config.abstract_model) ? edit_path(model_name: config.abstract_model.to_param, modal: true, id: '__ID__') : ''
8-
9-
xhr = !field.associated_collection_cache_all
10-
11-
collection = xhr ? [[field.formatted_value, field.selected_id]] : controller.list_entries(config, :index, field.associated_collection_scope, false).map { |o| [o.send(field.associated_object_label_method), o.send(field.associated_primary_key).to_s] }
12-
13-
js_data = {
14-
xhr: xhr,
15-
remote_source: index_path(config.abstract_model.to_param, source_object_id: form.object.id, source_abstract_model: source_abstract_model.to_param, associated_collection: field.name, current_action: current_action, compact: true),
16-
scopeBy: field.dynamic_scope_relationships
17-
}
183
%>
194

205
<div class="row">
216
<div class="col-sm-4">
22-
<% selected_id = (hdv = field.form_default_value).nil? ? field.selected_id : hdv %>
23-
<%= form.select field.method_name, collection, { selected: selected_id, include_blank: true }, field.html_attributes.reverse_merge({ data: { filteringselect: true, options: js_data.to_json }, placeholder: t('admin.misc.search') }) %>
7+
<%=
8+
form.select field.method_name, field.collection, { selected: field.form_value, include_blank: true },
9+
field.html_attributes.reverse_merge({ data: { filteringselect: true, options: field.widget_options.to_json }, placeholder: t('admin.misc.search') })
10+
%>
2411
</div>
2512
<div class="col-sm-8 mt-2 mt-md-0 modal-actions">
2613
<% if authorized?(:new, config.abstract_model) && field.inline_add %>
2714
<% path_hash = { model_name: config.abstract_model.to_param, modal: true }.merge!(field.associated_prepopulate_params) %>
2815
<%= link_to "<i class=\"fas fa-plus\"></i> ".html_safe + wording_for(:link, :new, config.abstract_model), '#', data: { link: new_path(path_hash) }, class: "btn btn-info create" %>
2916
<% end %>
30-
<% if edit_url.present? && field.inline_edit %>
31-
<%= link_to "<i class=\"fas fa-pencil-alt\"></i> ".html_safe + wording_for(:link, :edit, config.abstract_model), '#', data: { link: edit_url }, class: "btn btn-info update ms-2#{' disabled' if field.value.nil?}" %>
17+
<% if authorized?(:edit, config.abstract_model) && field.inline_edit %>
18+
<%= link_to "<i class=\"fas fa-pencil-alt\"></i> ".html_safe + wording_for(:link, :edit, config.abstract_model), '#', data: { link: edit_path(model_name: config.abstract_model.to_param, modal: true, id: '__ID__') }, class: "btn btn-info update ms-2#{' disabled' if field.value.nil?}" %>
3219
<% end %>
3320
</div>
3421
</div>
Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,23 @@
11
<%
2-
type_collection = field.polymorphic_type_collection
3-
type_column = field.association.foreign_type.to_s
4-
selected_type = field.bindings[:object].send(type_column)
5-
selected = field.bindings[:object].send(field.association.name)
6-
collection = selected ? [[field.formatted_value, selected.id]] : [[]]
7-
column_type_dom_id = form.dom_id(field).sub(field.method_name.to_s, type_column)
8-
current_action = params[:action].in?(['create', 'new']) ? 'create' : 'update'
9-
10-
default_options = { float_left: false }
11-
12-
js_data = type_collection.inject({}) do |options, model|
13-
source_abstract_model = RailsAdmin.config(form.object.class).abstract_model
14-
options.merge(model.second.downcase.gsub('::', '-') => {
15-
xhr: true,
16-
remote_source: index_path(model.second.underscore, source_object_id: form.object.id, source_abstract_model: source_abstract_model.to_param, current_action: current_action, compact: true),
17-
float_left: false
18-
})
19-
end
2+
column_type_dom_id = form.dom_id(field).sub(field.method_name.to_s, field.type_column)
203
%>
214

225
<div class="row">
236
<div class="col-sm-3">
24-
<% js_data.each do |model, value| %>
7+
<% field.widget_options_for_types.each do |model, value| %>
258
<div data-options="<%= value.to_json %>" id="<%= model %>-js-options"></div>
269
<% end %>
27-
<%= form.select type_column, type_collection, {include_blank: true, selected: selected_type}, class: "form-select", id: column_type_dom_id, data: { polymorphic: true, urls: field.polymorphic_type_urls.to_json }, style: "float: left; margin-right: 10px;" %>
10+
<%=
11+
form.select field.type_column, field.type_collection, {include_blank: true, selected: field.selected_type},
12+
class: "form-select", id: column_type_dom_id, data: { polymorphic: true, urls: field.type_urls.to_json },
13+
style: "float: left; margin-right: 10px;"
14+
%>
2815
</div>
2916
<div class="col-sm-4">
30-
<%= form.select field.method_name, collection, {include_blank: true, selected: selected.try(:id)}, class: "form-control", data: { filteringselect: true, options: js_data[selected_type.try(:downcase)] || default_options }, placeholder: 'Search' %>
17+
<%=
18+
form.select field.method_name, field.collection, {include_blank: true, selected: field.selected_id},
19+
class: "form-control", data: { filteringselect: true, options: field.widget_options },
20+
placeholder: 'Search'
21+
%>
3122
</div>
3223
</div>

lib/rails_admin/config/actions/index.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,11 @@ class Index < RailsAdmin::Config::Actions::Base
5050
format.json do
5151
output =
5252
if params[:compact]
53-
primary_key_method = @association ? @association.associated_primary_key : @model_config.abstract_model.primary_key
54-
label_method = @model_config.object_label_method
55-
@objects.collect { |o| {id: o.send(primary_key_method).to_s, label: o.send(label_method).to_s} }
53+
if @association
54+
@association.collection(@objects).collect { |(label, id)| {id: id, label: label} }
55+
else
56+
@objects.collect { |object| {id: object.id.to_s, label: object.send(@model_config.object_label_method).to_s} }
57+
end
5658
else
5759
@objects.to_json(@schema)
5860
end

lib/rails_admin/config/fields/association.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def association
1313
end
1414

1515
def method_name
16-
association.key_accessor
16+
nested_form ? :"#{name}_attributes" : association.key_accessor
1717
end
1818

1919
register_instance_option :pretty_value do
@@ -134,6 +134,12 @@ def value
134134
bindings[:object].send(association.name)
135135
end
136136

137+
# Returns collection of all selectable records
138+
def collection(scope = nil)
139+
(scope || bindings[:controller].list_entries(associated_model_config, :index, associated_collection_scope, false)).
140+
map { |o| [o.send(associated_object_label_method), o.send(associated_primary_key).to_s] }
141+
end
142+
137143
# has many?
138144
def multiple?
139145
true
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_admin/config/fields/association'
4+
5+
module RailsAdmin
6+
module Config
7+
module Fields
8+
class CollectionAssociation < Association
9+
# orderable associated objects
10+
register_instance_option :orderable do
11+
false
12+
end
13+
14+
register_instance_option :partial do
15+
nested_form ? :form_nested_many : :form_filtering_multiselect
16+
end
17+
18+
def collection(scope = nil)
19+
if scope
20+
super
21+
elsif associated_collection_cache_all
22+
selected = selected_ids
23+
i = 0
24+
super.sort_by { |a| [selected.index(a[1]) || selected.size, i += 1] }
25+
else
26+
value.map { |o| [o.send(associated_object_label_method), o.send(associated_primary_key)] }
27+
end
28+
end
29+
30+
def associated_prepopulate_params
31+
{associated_model_config.abstract_model.param_key => {association.foreign_key => bindings[:object].try(:id)}}
32+
end
33+
34+
def multiple?
35+
true
36+
end
37+
38+
def selected_ids
39+
value.map { |s| s.send(associated_primary_key).to_s }
40+
end
41+
42+
def form_default_value
43+
(default_value if bindings[:object].new_record? && value.empty?)
44+
end
45+
46+
def form_value
47+
form_default_value.nil? ? selected_ids : form_default_value
48+
end
49+
50+
def widget_options
51+
{
52+
xhr: !associated_collection_cache_all,
53+
'edit-url': (inline_edit && bindings[:view].authorized?(:edit, associated_model_config.abstract_model) ? bindings[:view].edit_path(model_name: associated_model_config.abstract_model.to_param, id: '__ID__') : ''),
54+
remote_source: bindings[:view].index_path(associated_model_config.abstract_model, source_object_id: bindings[:object].id, source_abstract_model: abstract_model.to_param, associated_collection: name, current_action: bindings[:view].current_action, compact: true),
55+
scopeBy: dynamic_scope_relationships,
56+
sortable: !!orderable,
57+
removable: !!removable,
58+
cacheAll: !!associated_collection_cache_all,
59+
regional: {
60+
add: ::I18n.t('admin.misc.add_new'),
61+
chooseAll: ::I18n.t('admin.misc.chose_all'),
62+
clearAll: ::I18n.t('admin.misc.clear_all'),
63+
down: ::I18n.t('admin.misc.down'),
64+
remove: ::I18n.t('admin.misc.remove'),
65+
search: ::I18n.t('admin.misc.search'),
66+
up: ::I18n.t('admin.misc.up'),
67+
},
68+
}
69+
end
70+
end
71+
end
72+
end
73+
end
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_admin/config/fields/association'
4+
5+
module RailsAdmin
6+
module Config
7+
module Fields
8+
class SingularAssociation < Association
9+
register_instance_option :filter_operators do
10+
%w[_discard like not_like is starts_with ends_with] + (required? ? [] : %w[_separator _present _blank])
11+
end
12+
13+
register_instance_option :formatted_value do
14+
(o = value) && o.send(associated_model_config.object_label_method)
15+
end
16+
17+
register_instance_option :partial do
18+
nested_form ? :form_nested_one : :form_filtering_select
19+
end
20+
21+
def collection(scope = nil)
22+
if associated_collection_cache_all || scope
23+
super
24+
else
25+
[[formatted_value, selected_id]]
26+
end
27+
end
28+
29+
def multiple?
30+
false
31+
end
32+
33+
def selected_id
34+
raise NoMethodError # abstract
35+
end
36+
37+
def form_value
38+
form_default_value.nil? ? selected_id : form_default_value
39+
end
40+
41+
def widget_options
42+
{
43+
xhr: !associated_collection_cache_all,
44+
remote_source: bindings[:view].index_path(associated_model_config.abstract_model, source_object_id: bindings[:object].id, source_abstract_model: abstract_model.to_param, associated_collection: name, current_action: bindings[:view].current_action, compact: true),
45+
scopeBy: dynamic_scope_relationships,
46+
}
47+
end
48+
end
49+
end
50+
end
51+
end

lib/rails_admin/config/fields/types/belongs_to_association.rb

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
# frozen_string_literal: true
22

3-
require 'rails_admin/config/fields/association'
3+
require 'rails_admin/config/fields/singular_association'
44

55
module RailsAdmin
66
module Config
77
module Fields
88
module Types
9-
class BelongsToAssociation < RailsAdmin::Config::Fields::Association
9+
class BelongsToAssociation < RailsAdmin::Config::Fields::SingularAssociation
1010
RailsAdmin::Config::Fields::Types.register(self)
1111

12-
register_instance_option :filter_operators do
13-
%w[_discard like not_like is starts_with ends_with] + (required? ? [] : %w[_separator _present _blank])
14-
end
15-
16-
register_instance_option :formatted_value do
17-
(o = value) && o.send(associated_model_config.object_label_method)
18-
end
19-
2012
register_instance_option :sortable do
2113
@sortable ||= abstract_model.adapter_supports_joins? && associated_model_config.abstract_model.properties.collect(&:name).include?(associated_model_config.object_label_method) ? associated_model_config.object_label_method : {abstract_model.table_name => method_name}
2214
end
@@ -25,25 +17,13 @@ class BelongsToAssociation < RailsAdmin::Config::Fields::Association
2517
@searchable ||= associated_model_config.abstract_model.properties.collect(&:name).include?(associated_model_config.object_label_method) ? [associated_model_config.object_label_method, {abstract_model.model => method_name}] : {abstract_model.model => method_name}
2618
end
2719

28-
register_instance_option :partial do
29-
nested_form ? :form_nested_one : :form_filtering_select
30-
end
31-
3220
register_instance_option :eager_load do
3321
true
3422
end
3523

3624
def selected_id
3725
bindings[:object].safe_send(association.key_accessor)
3826
end
39-
40-
def method_name
41-
nested_form ? :"#{name}_attributes" : super
42-
end
43-
44-
def multiple?
45-
false
46-
end
4727
end
4828
end
4929
end

lib/rails_admin/config/fields/types/has_and_belongs_to_many_association.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# frozen_string_literal: true
22

3-
require 'rails_admin/config/fields/types/has_many_association'
3+
require 'rails_admin/config/fields/collection_association'
44

55
module RailsAdmin
66
module Config
77
module Fields
88
module Types
9-
class HasAndBelongsToManyAssociation < RailsAdmin::Config::Fields::Types::HasManyAssociation
9+
class HasAndBelongsToManyAssociation < RailsAdmin::Config::Fields::CollectionAssociation
1010
# Register field type for the type loader
1111
RailsAdmin::Config::Fields::Types.register(self)
1212
end

0 commit comments

Comments
 (0)