-
Notifications
You must be signed in to change notification settings - Fork 67
Expand file tree
/
Copy pathnested_form_fields.rb
More file actions
130 lines (104 loc) · 5.31 KB
/
nested_form_fields.rb
File metadata and controls
130 lines (104 loc) · 5.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
require "nested_form_fields/version"
module NestedFormFields
module Rails
class Engine < ::Rails::Engine
end
end
end
module ActionView::Helpers
class FormBuilder
def nested_fields_for(record_name, record_object = nil, fields_options = {}, &block)
fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
fields_options[:builder] ||= options[:builder]
fields_options[:parent_builder] = self
fields_options[:wrapper_tag] ||= :fieldset
fields_options[:wrapper_options] ||= {}
fields_options[:namespace] = fields_options[:parent_builder].options[:namespace]
return fields_for_has_many_association_with_template(record_name, record_object, fields_options, block)
end
def add_nested_fields_link association, text = nil, html_options = {}, &block
html_options, text = text, nil if block_given? && text.is_a?(Hash)
html_class = html_options.delete(:class) || {}
html_data = html_options.delete(:data) || {}
args = []
args << (text || "Add #{association.to_s.singularize.humanize}") unless block_given?
args << ''
args << { class: "#{html_class.empty? ? '' : html_class} add_nested_fields_link",
data: { association_path: association_path(association.to_s),
object_class: association.to_s.singularize }.merge(html_data)
}.merge(html_options)
@template.link_to *args, &block
end
def remove_nested_fields_link text = nil, html_options = {}, &block
html_options, text = text, nil if block_given? && text.is_a?(Hash)
html_class = html_options.delete(:class) || {}
html_data = html_options.delete(:data) || {}
args = []
args << (text || 'x') unless block_given?
args << ''
args << { class: "#{html_class.empty? ? '' : html_class} remove_nested_fields_link",
data: { delete_association_field_name: delete_association_field_name,
object_class: @object.class.name.underscore.downcase }.merge(html_data)
}.merge(html_options)
@template.link_to *args, &block
end
private
def fields_for_has_many_association_with_template(association_name, association, options, block)
name = "#{object_name}[#{association_name}_attributes]"
association = convert_to_model(association)
if association.respond_to?(:persisted?)
association = [association]
elsif !association.respond_to?(:to_ary)
association = @object.send(association_name)
end
output = ActiveSupport::SafeBuffer.new
association.each_with_index do |child, index|
wrapper_options = options[:wrapper_options].clone || {}
if child._destroy == true
wrapper_options[:style] = wrapper_options[:style] ? wrapper_options[:style] + ';' + 'display:none' : 'display:none'
end
output << nested_fields_wrapper(association_name, options[:wrapper_tag], options[:legend], wrapper_options) do
new_block = fields_for_nested_model("#{name}[#{options[:child_index] || nested_child_index(name)}]", child, options, block)
# do substitution in user defined blocks with the current index allows JS functions to have proper references
new_block.gsub('__nested_field_for_replace_with_index__', index.to_s).html_safe
end
end
output << nested_model_template(name, association_name, options, block)
output
end
def nested_model_template name, association_name, options, block
@template.content_tag( :template, id: template_id(association_name)) do
nested_fields_wrapper(association_name, options[:wrapper_tag], options[:legend], options[:wrapper_options]) do
association_class = (options[:class_name] || object.public_send(association_name).klass.name).to_s.classify.constantize
fields_for_nested_model("#{name}[#{index_placeholder(association_name)}]",
association_class.new,
options, block)
end
end
end
def template_id association_name
"#{association_path(association_name)}_template"
end
def association_path association_name
"#{object_name.gsub('][','_').gsub(/_attributes/,'').sub('[','_').sub(']','')}_#{association_name}"
end
def index_placeholder association_name
"__#{association_path(association_name)}_index__"
end
def delete_association_field_name
"#{object_name}[_destroy]"
end
def nested_fields_wrapper(association_name, wrapper_element_type, legend, wrapper_options)
wrapper_options = add_default_classes_to_wrapper_options(association_name, wrapper_options.clone)
@template.content_tag wrapper_element_type, wrapper_options do
(wrapper_element_type==:fieldset && !legend.nil?)? ( @template.content_tag(:legend, legend, class: "nested_fields") + yield ) : yield
end
end
def add_default_classes_to_wrapper_options(association_name, wrapper_options)
default_classes = ["nested_fields", "nested_#{association_path(association_name)}"]
wrapper_options[:class] = wrapper_options[:class].is_a?(String) ? wrapper_options[:class].split(" ") : wrapper_options[:class].to_a
wrapper_options[:class] += default_classes
wrapper_options
end
end
end