nested_form 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.rdoc +12 -0
- data/README.md +165 -0
- data/Rakefile +7 -0
- data/lib/nested_form/builder_mixin.rb +13 -1
- data/spec/dummy/app/assets/javascripts/jquery_events_test.js +19 -0
- data/spec/dummy/app/assets/javascripts/jquery_nested_form.js +42 -10
- data/spec/dummy/app/assets/javascripts/prototype_events_test.js +20 -0
- data/spec/dummy/app/assets/javascripts/prototype_nested_form.js +37 -32
- data/spec/dummy/app/models/project.rb +2 -0
- data/spec/dummy/app/models/project_task.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +8 -0
- data/spec/dummy/app/views/projects/new.html.erb +0 -6
- data/spec/dummy/app/views/projects/without_intermediate_inputs.html.erb +11 -0
- data/spec/dummy/config/routes.rb +3 -1
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20120819164528_add_association_with_class_name.rb +12 -0
- data/spec/dummy/db/schema.rb +6 -1
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +91 -0
- data/spec/dummy/log/test.log +710 -25
- data/spec/dummy/tmp/cache/assets/C99/4D0/sprockets%2F5e30a6b911437f1428dc32c3ae182123 +0 -0
- data/spec/dummy/tmp/cache/assets/C99/7D0/sprockets%2F79513e6956e0ee8624976e041fd5636d +0 -0
- data/spec/dummy/tmp/cache/assets/CAA/C90/sprockets%2F1e6c8ee1258009385ccf5b84015424b3 +0 -0
- data/spec/dummy/tmp/cache/assets/CB2/CB0/sprockets%2F11cc8d161d71a716dd36f16849d90870 +0 -0
- data/spec/dummy/tmp/cache/assets/CB7/7F0/sprockets%2Fac97b043470f6fcc925c352f16956643 +0 -0
- data/spec/dummy/tmp/cache/assets/CDB/8A0/sprockets%2Faed2a2575c376263c26e93b56b57051d +0 -0
- data/spec/dummy/tmp/cache/assets/D1F/A10/sprockets%2F60317e62cb324bfd9987e8da9636fd06 +0 -0
- data/spec/dummy/tmp/cache/assets/D49/870/sprockets%2F90896142645585acc8baf56ad57e3afb +0 -0
- data/spec/dummy/tmp/cache/assets/D62/F00/sprockets%2F6ac03a007f26b1c18ed3d53498ad3eb8 +0 -0
- data/spec/dummy/tmp/cache/assets/D79/BC0/sprockets%2F85a1db977361cc5130fcefb4637ff67e +0 -0
- data/spec/dummy/tmp/cache/assets/D87/1F0/sprockets%2F61c9ceafb877fb740c67416ff6f782a9 +0 -0
- data/spec/dummy/tmp/cache/assets/DAD/4F0/sprockets%2F765acd1bf68dc90f7d3e61da78b1e659 +0 -0
- data/spec/dummy/tmp/cache/assets/E03/3F0/sprockets%2F82b37ae9eccec44c1ef44cfdd07d8534 +0 -0
- data/spec/dummy/tmp/cache/assets/E0A/CA0/sprockets%2F7fe72ac1c0db1a7e8e7f59cf25e8a39e +0 -0
- data/spec/dummy/tmp/cache/assets/E3A/A60/sprockets%2F7c72c96cfc66454caf5fc8ca0fedd4f3 +0 -0
- data/spec/dummy/tmp/cache/assets/E54/400/sprockets%2Fd0cbc16cc37efcf9dc41f242b5dbbf81 +0 -0
- data/spec/dummy/tmp/cache/assets/EB7/AB0/sprockets%2F801d29a5debdbfbfb4eef14d70d9bcdb +0 -0
- data/spec/dummy/tmp/cache/assets/F48/5E0/sprockets%2F26cd6ffcffbebbe2fd6cd1a8f0c2debc +0 -0
- data/spec/dummy/tmp/capybara/capybara-201208212241496415482929.html +31 -0
- data/spec/events_spec.rb +60 -0
- data/spec/form_spec.rb +9 -0
- data/spec/nested_form/builder_spec.rb +67 -19
- data/vendor/assets/javascripts/jquery_nested_form.js +42 -10
- data/vendor/assets/javascripts/prototype_nested_form.js +37 -32
- metadata +102 -72
- data/README.rdoc +0 -101
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
0.2.3 (August 23, 2012)
|
2
|
+
|
3
|
+
* Fix selector for deeply nested forms (thanks groe)
|
4
|
+
|
5
|
+
* Fix association detection in #link_to remove (thanks nashbridges)
|
6
|
+
|
7
|
+
* Add nested:fieldRemoved:type event (thanks nashbridges)
|
8
|
+
|
9
|
+
* Add events for Prototype (thanks nashbridges)
|
10
|
+
|
11
|
+
* Element.up() is the proper Prototype counter part to jQuery's closest() (thanks clemens)
|
12
|
+
|
1
13
|
0.2.2 (July 9, 2012)
|
2
14
|
|
3
15
|
* Make deeply-nested form working in jruby and rubinius
|
data/README.md
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
# Nested Form
|
2
|
+
|
3
|
+
[<img src="https://secure.travis-ci.org/ryanb/nested_form.png?branch=master" alt="Build Status" />](http://travis-ci.org/ryanb/nested_form)
|
4
|
+
|
5
|
+
This is a Rails gem for conveniently manage multiple nested models in a single form. It does so in an unobtrusive way through jQuery or Prototype.
|
6
|
+
|
7
|
+
This gem only works with Rails 3. See the [rails2 branch](https://github.com/ryanb/nested_form/tree/rails2) for a plugin to work in Rails 2.
|
8
|
+
|
9
|
+
An example project showing how this works is available in the [complex-nested-forms/nested_form branch](https://github.com/ryanb/complex-form-examples/tree/nested_form).
|
10
|
+
|
11
|
+
|
12
|
+
## Setup
|
13
|
+
|
14
|
+
Add it to your Gemfile then run `bundle` to install it.
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem "nested_form"
|
18
|
+
```
|
19
|
+
|
20
|
+
And then add it to the Asset Pipeline in the application.js file:
|
21
|
+
|
22
|
+
```
|
23
|
+
//= require jquery_nested_form
|
24
|
+
```
|
25
|
+
|
26
|
+
### Non Asset Pipeline Setup
|
27
|
+
|
28
|
+
If you do not use the asset pipeline, run this generator to create the JavaScript file.
|
29
|
+
|
30
|
+
```
|
31
|
+
rails g nested_form:install
|
32
|
+
```
|
33
|
+
|
34
|
+
You can then include the generated JavaScript in your layout.
|
35
|
+
|
36
|
+
```erb
|
37
|
+
<%= javascript_include_tag :defaults, "nested_form" %>
|
38
|
+
```
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
Imagine you have a `Project` model that `has_many :tasks`. To be able to use this gem, you'll need to add `accepts_nested_attributes_for :tasks` to your Project model. If you wish to allow the nested objects to be destroyed, then add the `:allow_destroy => true` option to that declaration. If you don't have the `accepts_nested_attributes_for :tasks` you'll get a Missing Block Error. See the [accepts_nested_attributes_for documentation](http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html#method-i-accepts_nested_attributes_for) for details on all available options.
|
43
|
+
|
44
|
+
This will create a `tasks_attributes=` method, so you may need to add it to the `attr_accessible` array (`attr_accessible :tasks_attributes`).
|
45
|
+
|
46
|
+
Then use the `nested_form_for` helper method to enable the nesting.
|
47
|
+
|
48
|
+
```erb
|
49
|
+
<%= nested_form_for @project do |f| %>
|
50
|
+
```
|
51
|
+
|
52
|
+
You will then be able to use `link_to_add` and `link_to_remove` helper methods on the form builder in combination with fields_for to dynamically add/remove nested records.
|
53
|
+
|
54
|
+
```erb
|
55
|
+
<%= f.fields_for :tasks do |task_form| %>
|
56
|
+
<%= task_form.text_field :name %>
|
57
|
+
<%= task_form.link_to_remove "Remove this task" %>
|
58
|
+
<% end %>
|
59
|
+
<p><%= f.link_to_add "Add a task", :tasks %></p>
|
60
|
+
```
|
61
|
+
|
62
|
+
|
63
|
+
## SimpleForm and Formtastic Support
|
64
|
+
|
65
|
+
Use `simple_nested_form_for` or `semantic_nested_form_for` for SimpleForm and Formtastic support respectively.
|
66
|
+
|
67
|
+
|
68
|
+
## Partials
|
69
|
+
|
70
|
+
It is often desirable to move the nested fields into a partial to keep things organized. If you don't supply a block to fields_for it will look for a partial and use that.
|
71
|
+
|
72
|
+
```erb
|
73
|
+
<%= f.fields_for :tasks %>
|
74
|
+
```
|
75
|
+
|
76
|
+
In this case it will look for a partial called "task_fields" and pass the form builder as an `f` variable to it.
|
77
|
+
|
78
|
+
|
79
|
+
## JavaScript events
|
80
|
+
|
81
|
+
Sometimes you want to do some additional work after element was added or removed, but only
|
82
|
+
after DOM was _really_ modified. In this case simply listening for click events on
|
83
|
+
'Add new'/'Remove' link won't reliably work, because your code and code that inserts/removes
|
84
|
+
nested field will run concurrently.
|
85
|
+
|
86
|
+
This problem can be solved, because after adding or removing the field a set of custom events
|
87
|
+
is triggered on this field. Using form example from above, if you click on the "Add a task" link,
|
88
|
+
`nested:fieldAdded` and `nested:fieldAdded:tasks` will be triggered, while
|
89
|
+
`nested:fieldRemoved` and `nested:fieldRemoved:tasks` will be triggered if you click
|
90
|
+
"Remove this task" then.
|
91
|
+
|
92
|
+
These events bubble up the DOM tree, going through `form` element, until they reach the `document`.
|
93
|
+
This allows you to listen for the event and trigger some action accordingly. Field element, upon
|
94
|
+
which action was made, is passed along with the `event` object. In jQuery you can access it
|
95
|
+
via `event.field`, in Prototype the same field will be in `event.memo.field`.
|
96
|
+
|
97
|
+
For example, you have a date input in a nested field and you want to use jQuery datepicker
|
98
|
+
for it. This is a bit tricky, because you have to activate datepicker after field was inserted.
|
99
|
+
|
100
|
+
### jQuery
|
101
|
+
|
102
|
+
```javascript
|
103
|
+
$(document).on('nested:fieldAdded', function(event){
|
104
|
+
// this field was just inserted into your form
|
105
|
+
var field = event.field;
|
106
|
+
// it's a jQuery object already! Now you can find date input
|
107
|
+
var dateField = field.find('.date');
|
108
|
+
// and activate datepicker on it
|
109
|
+
dateField.datepicker();
|
110
|
+
})
|
111
|
+
```
|
112
|
+
|
113
|
+
### Prototype
|
114
|
+
|
115
|
+
```javascript
|
116
|
+
document.observe('nested:fieldAdded', function(event){
|
117
|
+
var field = event.memo.field;
|
118
|
+
// it's already extended by Prototype
|
119
|
+
var dateField = field.down('.date');
|
120
|
+
dateField.datepicker();
|
121
|
+
})
|
122
|
+
```
|
123
|
+
|
124
|
+
Second type of event (i.e. `nested:fieldAdded:tasks`) is useful then you have more than one type
|
125
|
+
of nested fields on a form (i.e. tasks and milestones) and want to distinguish, which exactly
|
126
|
+
was added/deleted.
|
127
|
+
|
128
|
+
See also [how to limit max count of nested fields](https://github.com/ryanb/nested_form/wiki/How-to:-limit-max-count-of-nested-fields)
|
129
|
+
|
130
|
+
## Enhanced jQuery JavaScript template
|
131
|
+
|
132
|
+
You can override default behavior of inserting new subforms into your form. For example:
|
133
|
+
|
134
|
+
```javascript
|
135
|
+
window.nestedFormEvents.insertFields = function(content, assoc, link) {
|
136
|
+
return $(link).closest('form').find(assoc + '_fields').append($(content));
|
137
|
+
}
|
138
|
+
```
|
139
|
+
|
140
|
+
## Project Status
|
141
|
+
|
142
|
+
Unfortunately I have not had time to actively work on this project recently. If you find a critical issue where it does not work as documented please [ping me on Twitter](http://twitter.com/rbates) and I'll take a look.
|
143
|
+
|
144
|
+
## Contributing
|
145
|
+
|
146
|
+
If you have any issues with Nested Form not addressed above or in the [example project](http://github.com/ryanb/complex-form-examples/tree/nested_form), please add an [issue on GitHub](http://github.com/ryanb/nested_form/issues) or [fork the project](http://help.github.com/fork-a-repo) and send a [pull request](http://help.github.com/send-pull-requests). To run the specs:
|
147
|
+
|
148
|
+
```
|
149
|
+
bundle install
|
150
|
+
bundle exec rake spec:install
|
151
|
+
bundle exec rake db:migrate
|
152
|
+
bundle exec rake spec:all
|
153
|
+
```
|
154
|
+
|
155
|
+
See available rake tasks using `bundle exec rake -T`.
|
156
|
+
|
157
|
+
## Special Thanks
|
158
|
+
|
159
|
+
This gem was originally based on the solution by Tim Riley in his [complex-form-examples fork](https://github.com/timriley/complex-form-examples/tree/unobtrusive-jquery-deep-fix2).
|
160
|
+
|
161
|
+
Thank you Andrew Manshin for the Rails 3 transition, [Andrea Singh](https://github.com/madebydna) for converting to a gem and [Peter Giacomo Lombardo](https://github.com/pglombardo) for Prototype support.
|
162
|
+
|
163
|
+
Andrea also wrote a great [blog post](http://blog.madebydna.com/all/code/2010/10/07/dynamic-nested-froms-with-the-nested-form-gem.html) on the internal workings of this gem.
|
164
|
+
|
165
|
+
Thanks [Pavel Forkert](https://github.com/fxposter) for the SimpleForm and Formtastic support.
|
data/Rakefile
CHANGED
@@ -14,26 +14,33 @@ end
|
|
14
14
|
task :default => :spec
|
15
15
|
|
16
16
|
namespace :db do
|
17
|
+
desc 'Prepare sqlite database'
|
17
18
|
task :migrate do
|
18
19
|
system 'cd spec/dummy && rake db:migrate RAILS_ENV=test && rake db:migrate RAILS_ENV=development'
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
22
23
|
namespace :spec do
|
24
|
+
desc 'Install gems from additional gemfiles'
|
23
25
|
task :install do
|
24
26
|
system 'bundle install'
|
27
|
+
ENV.delete('GEM_HOME')
|
25
28
|
ENV['BUNDLE_GEMFILE'] = File.expand_path('../gemfiles/Gemfile.rails3_1', __FILE__)
|
26
29
|
system 'bundle install'
|
27
30
|
ENV['BUNDLE_GEMFILE'] = File.expand_path('../gemfiles/Gemfile.rails3_0', __FILE__)
|
28
31
|
system 'bundle install'
|
29
32
|
end
|
30
33
|
|
34
|
+
desc 'Run tests with Rails 3.1.x'
|
31
35
|
task :rails3_1 do
|
36
|
+
ENV.delete('GEM_HOME')
|
32
37
|
ENV['BUNDLE_GEMFILE'] = File.expand_path('../gemfiles/Gemfile.rails3_1', __FILE__)
|
33
38
|
Rake::Task["spec"].execute
|
34
39
|
end
|
35
40
|
|
41
|
+
desc 'Run tests with Rails 3.0.x'
|
36
42
|
task :rails3_0 do
|
43
|
+
ENV.delete('GEM_HOME')
|
37
44
|
ENV['BUNDLE_GEMFILE'] = File.expand_path('../gemfiles/Gemfile.rails3_0', __FILE__)
|
38
45
|
Rake::Task["spec"].execute
|
39
46
|
end
|
@@ -14,13 +14,19 @@ module NestedForm
|
|
14
14
|
def link_to_add(*args, &block)
|
15
15
|
options = args.extract_options!.symbolize_keys
|
16
16
|
association = args.pop
|
17
|
+
|
18
|
+
unless (reflection = object.class.reflect_on_association(association))
|
19
|
+
raise ArgumentError, "Failed to find #{object.class.name} association by name \"#{association}\""
|
20
|
+
end
|
21
|
+
model_object = reflection.klass.new
|
22
|
+
|
17
23
|
options[:class] = [options[:class], "add_nested_fields"].compact.join(" ")
|
18
24
|
options["data-association"] = association
|
19
25
|
args << (options.delete(:href) || "javascript:void(0)")
|
20
26
|
args << options
|
27
|
+
|
21
28
|
@fields ||= {}
|
22
29
|
@template.after_nested_form(association) do
|
23
|
-
model_object = object.class.reflect_on_association(association).klass.new
|
24
30
|
blueprint = fields_for(association, model_object, :child_index => "new_#{association}", &@fields[association])
|
25
31
|
blueprint_options = {:id => "#{association}_fields_blueprint", :style => 'display: none'}
|
26
32
|
@template.content_tag(:div, blueprint, blueprint_options)
|
@@ -42,6 +48,12 @@ module NestedForm
|
|
42
48
|
def link_to_remove(*args, &block)
|
43
49
|
options = args.extract_options!.symbolize_keys
|
44
50
|
options[:class] = [options[:class], "remove_nested_fields"].compact.join(" ")
|
51
|
+
|
52
|
+
# Extracting "milestones" from "...[milestones_attributes][...]"
|
53
|
+
md = object_name.to_s.match /(\w+)_attributes\]\[[\w\d]+\]$/
|
54
|
+
association = md && md[1]
|
55
|
+
options["data-association"] = association
|
56
|
+
|
45
57
|
args << (options.delete(:href) || "javascript:void(0)")
|
46
58
|
args << options
|
47
59
|
(hidden_field(:_destroy) << @template.link_to(*args, &block)).html_safe
|
@@ -0,0 +1,19 @@
|
|
1
|
+
$(function() {
|
2
|
+
var log = function(text) {
|
3
|
+
$('<p/>', {text: text}).appendTo('#console');
|
4
|
+
};
|
5
|
+
|
6
|
+
['Added', 'Removed'].forEach(function(action) {
|
7
|
+
$(document).on('nested:field' + action, function(e) {
|
8
|
+
log(action + ' some field')
|
9
|
+
});
|
10
|
+
|
11
|
+
$(document).on('nested:field' + action + ':tasks', function(e) {
|
12
|
+
log(action + ' task field')
|
13
|
+
});
|
14
|
+
|
15
|
+
$(document).on('nested:field' + action + ':milestones', function(e) {
|
16
|
+
log(action + ' milestone field')
|
17
|
+
});
|
18
|
+
});
|
19
|
+
});
|
@@ -13,7 +13,7 @@ jQuery(function($) {
|
|
13
13
|
|
14
14
|
// Make the context correct by replacing new_<parents> with the generated ID
|
15
15
|
// of each of the parent objects
|
16
|
-
var context = ($(link).closest('.fields').
|
16
|
+
var context = ($(link).closest('.fields').closestChild('input, textarea').eq(0).attr('name') || '').replace(new RegExp('\[[a-z]+\]$'), '');
|
17
17
|
|
18
18
|
// context will be something like this for a brand new form:
|
19
19
|
// project[tasks_attributes][new_1255929127459][assignments_attributes][new_1255929128105]
|
@@ -42,7 +42,8 @@ jQuery(function($) {
|
|
42
42
|
content = content.replace(regexp, "new_" + new_id);
|
43
43
|
|
44
44
|
var field = this.insertFields(content, assoc, link);
|
45
|
-
|
45
|
+
// bubble up event upto document (through form)
|
46
|
+
field
|
46
47
|
.trigger({ type: 'nested:fieldAdded', field: field })
|
47
48
|
.trigger({ type: 'nested:fieldAdded:' + assoc, field: field });
|
48
49
|
return false;
|
@@ -51,16 +52,18 @@ jQuery(function($) {
|
|
51
52
|
return $(content).insertBefore(link);
|
52
53
|
},
|
53
54
|
removeFields: function(e) {
|
54
|
-
var link = e.currentTarget
|
55
|
-
|
55
|
+
var $link = $(e.currentTarget),
|
56
|
+
assoc = $link.data('association'); // Name of child to be removed
|
57
|
+
|
58
|
+
var hiddenField = $link.prev('input[type=hidden]');
|
56
59
|
hiddenField.val('1');
|
57
|
-
|
58
|
-
|
59
|
-
// hiddenField.value = '1';
|
60
|
-
// }
|
61
|
-
var field = $(link).closest('.fields');
|
60
|
+
|
61
|
+
var field = $link.closest('.fields');
|
62
62
|
field.hide();
|
63
|
-
|
63
|
+
|
64
|
+
field
|
65
|
+
.trigger({ type: 'nested:fieldRemoved', field: field })
|
66
|
+
.trigger({ type: 'nested:fieldRemoved:' + assoc, field: field });
|
64
67
|
return false;
|
65
68
|
}
|
66
69
|
};
|
@@ -69,3 +72,32 @@ jQuery(function($) {
|
|
69
72
|
$('form a.add_nested_fields').live('click', nestedFormEvents.addFields);
|
70
73
|
$('form a.remove_nested_fields').live('click', nestedFormEvents.removeFields);
|
71
74
|
});
|
75
|
+
// http://plugins.jquery.com/project/closestChild
|
76
|
+
/*
|
77
|
+
* Copyright 2011, Tobias Lindig
|
78
|
+
*
|
79
|
+
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
|
80
|
+
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
|
81
|
+
*
|
82
|
+
*/
|
83
|
+
(function($) {
|
84
|
+
$.fn.closestChild = function(selector) {
|
85
|
+
// breadth first search for the first matched node
|
86
|
+
if (selector && selector != '') {
|
87
|
+
var queue = [];
|
88
|
+
queue.push(this);
|
89
|
+
while(queue.length > 0) {
|
90
|
+
var node = queue.shift();
|
91
|
+
var children = node.children();
|
92
|
+
for(var i = 0; i < children.length; ++i) {
|
93
|
+
var child = $(children[i]);
|
94
|
+
if (child.is(selector)) {
|
95
|
+
return child; //well, we found one
|
96
|
+
}
|
97
|
+
queue.push(child);
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
101
|
+
return $();//nothing found
|
102
|
+
};
|
103
|
+
})(jQuery);
|
@@ -0,0 +1,20 @@
|
|
1
|
+
document.observe('dom:loaded', function() {
|
2
|
+
var log = function(text) {
|
3
|
+
var p = new Element('p').update(text);
|
4
|
+
$('console').insert(p);
|
5
|
+
};
|
6
|
+
|
7
|
+
['Added', 'Removed'].forEach(function(action) {
|
8
|
+
document.observe('nested:field' + action, function(e) {
|
9
|
+
log(action + ' some field')
|
10
|
+
});
|
11
|
+
|
12
|
+
document.observe('nested:field' + action + ':tasks', function(e) {
|
13
|
+
log(action + ' task field')
|
14
|
+
});
|
15
|
+
|
16
|
+
document.observe('nested:field' + action + ':milestones', function(e) {
|
17
|
+
log(action + ' milestone field')
|
18
|
+
});
|
19
|
+
});
|
20
|
+
});
|
@@ -1,22 +1,22 @@
|
|
1
1
|
document.observe('click', function(e, el) {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
if (el = e.findElement('form a.add_nested_fields')) {
|
3
|
+
// Setup
|
4
|
+
var assoc = el.readAttribute('data-association'); // Name of child
|
5
|
+
var content = $(assoc + '_fields_blueprint').innerHTML; // Fields template
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
// Make the context correct by replacing new_<parents> with the generated ID
|
8
|
+
// of each of the parent objects
|
9
|
+
var context = (el.getOffsetParent('.fields').firstDescendant().readAttribute('name') || '').replace(new RegExp('\[[a-z]+\]$'), '');
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
// context will be something like this for a brand new form:
|
12
|
+
// project[tasks_attributes][new_1255929127459][assignments_attributes][new_1255929128105]
|
13
|
+
// or for an edit form:
|
14
|
+
// project[tasks_attributes][0][assignments_attributes][1]
|
15
|
+
if(context) {
|
16
|
+
var parent_names = context.match(/[a-z_]+_attributes/g) || [];
|
17
|
+
var parent_ids = context.match(/(new_)?[0-9]+/g) || [];
|
18
18
|
|
19
|
-
|
19
|
+
for(i = 0; i < parent_names.length; i++) {
|
20
20
|
if(parent_ids[i]) {
|
21
21
|
content = content.replace(
|
22
22
|
new RegExp('(_' + parent_names[i] + ')_.+?_', 'g'),
|
@@ -26,26 +26,31 @@ document.observe('click', function(e, el) {
|
|
26
26
|
new RegExp('(\\[' + parent_names[i] + '\\])\\[.+?\\]', 'g'),
|
27
27
|
'$1[' + parent_ids[i] + ']');
|
28
28
|
}
|
29
|
-
|
30
|
-
|
29
|
+
}
|
30
|
+
}
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
// Make a unique ID for the new child
|
33
|
+
var regexp = new RegExp('new_' + assoc, 'g');
|
34
|
+
var new_id = new Date().getTime();
|
35
|
+
content = content.replace(regexp, "new_" + new_id);
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
var field = el.insert({ before: content });
|
38
|
+
field.fire('nested:fieldAdded', {field: field});
|
39
|
+
field.fire('nested:fieldAdded:' + assoc, {field: field});
|
40
|
+
return false;
|
41
|
+
}
|
40
42
|
});
|
41
43
|
|
42
44
|
document.observe('click', function(e, el) {
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
45
|
+
if (el = e.findElement('form a.remove_nested_fields')) {
|
46
|
+
var hidden_field = el.previous(0),
|
47
|
+
assoc = el.readAttribute('data-association'); // Name of child to be removed
|
48
|
+
if(hidden_field) {
|
49
|
+
hidden_field.value = '1';
|
50
|
+
}
|
51
|
+
var field = el.up('.fields').hide();
|
52
|
+
field.fire('nested:fieldRemoved', {field: field});
|
53
|
+
field.fire('nested:fieldRemoved:' + assoc, {field: field});
|
54
|
+
return false;
|
55
|
+
}
|
51
56
|
});
|