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.
Files changed (46) hide show
  1. data/CHANGELOG.rdoc +12 -0
  2. data/README.md +165 -0
  3. data/Rakefile +7 -0
  4. data/lib/nested_form/builder_mixin.rb +13 -1
  5. data/spec/dummy/app/assets/javascripts/jquery_events_test.js +19 -0
  6. data/spec/dummy/app/assets/javascripts/jquery_nested_form.js +42 -10
  7. data/spec/dummy/app/assets/javascripts/prototype_events_test.js +20 -0
  8. data/spec/dummy/app/assets/javascripts/prototype_nested_form.js +37 -32
  9. data/spec/dummy/app/models/project.rb +2 -0
  10. data/spec/dummy/app/models/project_task.rb +3 -0
  11. data/spec/dummy/app/views/layouts/application.html.erb +8 -0
  12. data/spec/dummy/app/views/projects/new.html.erb +0 -6
  13. data/spec/dummy/app/views/projects/without_intermediate_inputs.html.erb +11 -0
  14. data/spec/dummy/config/routes.rb +3 -1
  15. data/spec/dummy/db/development.sqlite3 +0 -0
  16. data/spec/dummy/db/migrate/20120819164528_add_association_with_class_name.rb +12 -0
  17. data/spec/dummy/db/schema.rb +6 -1
  18. data/spec/dummy/db/test.sqlite3 +0 -0
  19. data/spec/dummy/log/development.log +91 -0
  20. data/spec/dummy/log/test.log +710 -25
  21. data/spec/dummy/tmp/cache/assets/C99/4D0/sprockets%2F5e30a6b911437f1428dc32c3ae182123 +0 -0
  22. data/spec/dummy/tmp/cache/assets/C99/7D0/sprockets%2F79513e6956e0ee8624976e041fd5636d +0 -0
  23. data/spec/dummy/tmp/cache/assets/CAA/C90/sprockets%2F1e6c8ee1258009385ccf5b84015424b3 +0 -0
  24. data/spec/dummy/tmp/cache/assets/CB2/CB0/sprockets%2F11cc8d161d71a716dd36f16849d90870 +0 -0
  25. data/spec/dummy/tmp/cache/assets/CB7/7F0/sprockets%2Fac97b043470f6fcc925c352f16956643 +0 -0
  26. data/spec/dummy/tmp/cache/assets/CDB/8A0/sprockets%2Faed2a2575c376263c26e93b56b57051d +0 -0
  27. data/spec/dummy/tmp/cache/assets/D1F/A10/sprockets%2F60317e62cb324bfd9987e8da9636fd06 +0 -0
  28. data/spec/dummy/tmp/cache/assets/D49/870/sprockets%2F90896142645585acc8baf56ad57e3afb +0 -0
  29. data/spec/dummy/tmp/cache/assets/D62/F00/sprockets%2F6ac03a007f26b1c18ed3d53498ad3eb8 +0 -0
  30. data/spec/dummy/tmp/cache/assets/D79/BC0/sprockets%2F85a1db977361cc5130fcefb4637ff67e +0 -0
  31. data/spec/dummy/tmp/cache/assets/D87/1F0/sprockets%2F61c9ceafb877fb740c67416ff6f782a9 +0 -0
  32. data/spec/dummy/tmp/cache/assets/DAD/4F0/sprockets%2F765acd1bf68dc90f7d3e61da78b1e659 +0 -0
  33. data/spec/dummy/tmp/cache/assets/E03/3F0/sprockets%2F82b37ae9eccec44c1ef44cfdd07d8534 +0 -0
  34. data/spec/dummy/tmp/cache/assets/E0A/CA0/sprockets%2F7fe72ac1c0db1a7e8e7f59cf25e8a39e +0 -0
  35. data/spec/dummy/tmp/cache/assets/E3A/A60/sprockets%2F7c72c96cfc66454caf5fc8ca0fedd4f3 +0 -0
  36. data/spec/dummy/tmp/cache/assets/E54/400/sprockets%2Fd0cbc16cc37efcf9dc41f242b5dbbf81 +0 -0
  37. data/spec/dummy/tmp/cache/assets/EB7/AB0/sprockets%2F801d29a5debdbfbfb4eef14d70d9bcdb +0 -0
  38. data/spec/dummy/tmp/cache/assets/F48/5E0/sprockets%2F26cd6ffcffbebbe2fd6cd1a8f0c2debc +0 -0
  39. data/spec/dummy/tmp/capybara/capybara-201208212241496415482929.html +31 -0
  40. data/spec/events_spec.rb +60 -0
  41. data/spec/form_spec.rb +9 -0
  42. data/spec/nested_form/builder_spec.rb +67 -19
  43. data/vendor/assets/javascripts/jquery_nested_form.js +42 -10
  44. data/vendor/assets/javascripts/prototype_nested_form.js +37 -32
  45. metadata +102 -72
  46. data/README.rdoc +0 -101
@@ -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
@@ -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').find('input:first').attr('name') || '').replace(new RegExp('\[[a-z]+\]$'), '');
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
- $(link).closest("form")
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
- var hiddenField = $(link).prev('input[type=hidden]');
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
- // if (hiddenField) {
58
- // $(link).v
59
- // hiddenField.value = '1';
60
- // }
61
- var field = $(link).closest('.fields');
60
+
61
+ var field = $link.closest('.fields');
62
62
  field.hide();
63
- $(link).closest("form").trigger({ type: 'nested:fieldRemoved', field: field });
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
- 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
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
- // 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]+\]$'), '');
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
- // 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) || [];
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
- for(i = 0; i < parent_names.length; i++) {
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
- // 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);
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
- el.insert({ before: content });
38
- return false;
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
- if (el = e.findElement('form a.remove_nested_fields')) {
44
- var hidden_field = el.previous(0);
45
- if(hidden_field) {
46
- hidden_field.value = '1';
47
- }
48
- el.ancestors()[0].hide();
49
- return false;
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
  });