nested_form 0.0.0 → 0.1.0

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 ADDED
@@ -0,0 +1,16 @@
1
+ 0.1.0 (March 26, 2011)
2
+
3
+ * Prefix new records with "new_" so there's no possible conflict with existing records - issue #21
4
+
5
+ * Add support for _fields partial if no block is passed in to fields_for
6
+
7
+ * Use the $-jquery-function only inside the jQuery scope (thanks nhocki)
8
+
9
+ * Triggers nested:fieldAdded and nested:fieldRemoved events (thanks pirelenito)
10
+
11
+ * Fixed JavaScript bug for nested attributes in has_one association (thanks pirelenito)
12
+
13
+
14
+ 0.0.0 (February 17, 2011)
15
+
16
+ * Initial release
data/README.rdoc CHANGED
@@ -1,44 +1,38 @@
1
1
  = Nested Form
2
2
 
3
- A Rails gem to conveniently manage multiple nested models in a single form. It does so in an unobtrusive way through jQuery.
3
+ 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.
4
4
 
5
- To learn more about how this works under the hood: http://blog.madebydna.com/dynamic-nested-forms-in-rails-3-with-the-nest
5
+ 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.
6
6
 
7
7
 
8
- == Install
8
+ == Setup
9
9
 
10
- Add it to your Gemfile
11
-
12
- gem "nested_form"
10
+ Add it to your Gemfile then run +bundle+ to install it.
13
11
 
14
- Run
15
-
16
- bundle install
12
+ gem "nested_form"
17
13
 
18
- Run the generator
14
+ Next run the generator to create the JavaScript file. This will automatically detect if you are using jQuery or Prototype.
19
15
 
20
- rails generate nested_form:install
16
+ rails g nested_form:install
21
17
 
18
+ Running the generator will add a file at <tt>public/javascripts/nested_form.js</tt> which should be included after the jQuery or Prototype framework.
22
19
 
23
- == Usage
20
+ <%= javascript_include_tag :defaults, "nested_form" %>
24
21
 
25
- Running the generator will add a file at public/javascripts/nested_form.js which should be included after the jQuery or Prototype framework.
26
22
 
27
- <%= javascript_include_tag :defaults, "nested_form" %>
23
+ == Usage
28
24
 
29
- You can then generate a nested form using the nested_form_for helper method.
25
+ Use the +nested_form_for+ helper method to enable the nesting.
30
26
 
31
27
  <%= nested_form_for @project do |f| %>
32
28
 
33
- Use this form just like normal, including the +fields_for+ helper method for nesting models. The benefit of this plugin comes from the +link_to_add+ and +link_to_remove+ helper methods on the form builder.
29
+ 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.
34
30
 
35
31
  <%= f.fields_for :tasks do |task_form| %>
36
32
  <%= task_form.text_field :name %>
37
33
  <%= task_form.link_to_remove "Remove this task" %>
38
34
  <% end %>
39
- <%= f.link_to_add "Add a task", :tasks %>
40
-
41
- This generates links which dynamically add and remove fields.
35
+ <p><%= f.link_to_add "Add a task", :tasks %></p>
42
36
 
43
37
 
44
38
  == Partials
@@ -50,8 +44,15 @@ It is often desirable to move the nested fields into a partial to keep things or
50
44
  In this case it will look for a partial called "task_fields" and pass the form builder as an f variable to it.
51
45
 
52
46
 
47
+ == Events
48
+
49
+ If you are using jQuery, <tt>nested:fieldAdded<tt> and <tt>nested:fieldRemoved</tt> events are triggered on the +form+ element after adding and removing fields.
50
+
51
+
53
52
  == Special Thanks
54
53
 
55
54
  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].
56
55
 
57
56
  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.
57
+
58
+ 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.
@@ -4,7 +4,7 @@ module NestedForm
4
4
  def self.source_root
5
5
  File.dirname(__FILE__) + "/templates"
6
6
  end
7
-
7
+
8
8
  def copy_jquery_file
9
9
  if File.exists?('public/javascripts/prototype.js')
10
10
  copy_file 'prototype_nested_form.js', 'public/javascripts/nested_form.js'
@@ -1,46 +1,48 @@
1
- $(function() {
2
- $('form a.add_nested_fields').live('click', function() {
3
- // Setup
4
- var assoc = $(this).attr('data-association'); // Name of child
5
- var content = $('#' + assoc + '_fields_blueprint').html(); // Fields template
1
+ jQuery(function($) {
2
+ $('form a.add_nested_fields').live('click', function() {
3
+ // Setup
4
+ var assoc = $(this).attr('data-association'); // Name of child
5
+ var content = $('#' + assoc + '_fields_blueprint').html(); // 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 = ($(this).closest('.fields').find('input:first').attr('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 = ($(this).closest('.fields').find('input:first').attr('name') || '').replace(new RegExp('\[[a-z]+\]$'), '');
10
10
 
11
- // context will be something like this for a brand new form:
12
- // project[tasks_attributes][1255929127459][assignments_attributes][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(/[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++) {
20
- if(parent_ids[i]) {
21
- content = content.replace(
22
- new RegExp('(\\[' + parent_names[i] + '\\])\\[.+?\\]', 'g'),
23
- '$1[' + parent_ids[i] + ']'
24
- )
19
+ for(i = 0; i < parent_names.length; i++) {
20
+ if(parent_ids[i]) {
21
+ content = content.replace(
22
+ new RegExp('(\\[' + parent_names[i] + '\\])\\[.+?\\]', 'g'),
23
+ '$1[' + parent_ids[i] + ']'
24
+ )
25
+ }
25
26
  }
26
27
  }
27
- }
28
28
 
29
- // Make a unique ID for the new child
30
- var regexp = new RegExp('new_' + assoc, 'g');
31
- var new_id = new Date().getTime();
32
- content = content.replace(regexp, new_id);
29
+ // Make a unique ID for the new child
30
+ var regexp = new RegExp('new_' + assoc, 'g');
31
+ var new_id = new Date().getTime();
32
+ content = content.replace(regexp, "new_" + new_id);
33
33
 
34
- $(this).before(content);
35
- return false;
36
- });
34
+ $(this).before(content);
35
+ $(this).closest("form").trigger('nested:fieldAdded');
36
+ return false;
37
+ });
37
38
 
38
- $('form a.remove_nested_fields').live('click', function() {
39
- var hidden_field = $(this).prev('input[type=hidden]')[0];
40
- if(hidden_field) {
41
- hidden_field.value = '1';
42
- }
43
- $(this).closest('.fields').hide();
44
- return false;
39
+ $('form a.remove_nested_fields').live('click', function() {
40
+ var hidden_field = $(this).prev('input[type=hidden]')[0];
41
+ if(hidden_field) {
42
+ hidden_field.value = '1';
43
+ }
44
+ $(this).closest('.fields').hide();
45
+ $(this).closest("form").trigger('nested:fieldRemoved');
46
+ return false;
47
+ });
45
48
  });
46
- });
@@ -9,12 +9,12 @@ document.observe('click', function(e, el) {
9
9
  var context = (el.getOffsetParent('.fields').firstDescendant().readAttribute('name') || '').replace(new RegExp('\[[a-z]+\]$'), '');
10
10
 
11
11
  // context will be something like this for a brand new form:
12
- // project[tasks_attributes][1255929127459][assignments_attributes][1255929128105]
12
+ // project[tasks_attributes][new_1255929127459][assignments_attributes][new_1255929128105]
13
13
  // or for an edit form:
14
14
  // project[tasks_attributes][0][assignments_attributes][1]
15
15
  if(context) {
16
16
  var parent_names = context.match(/[a-z_]+_attributes/g) || [];
17
- var parent_ids = context.match(/[0-9]+/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]) {
@@ -29,7 +29,7 @@ document.observe('click', function(e, el) {
29
29
  // Make a unique ID for the new child
30
30
  var regexp = new RegExp('new_' + assoc, 'g');
31
31
  var new_id = new Date().getTime();
32
- content = content.replace(regexp, new_id);
32
+ content = content.replace(regexp, "new_" + new_id);
33
33
 
34
34
  el.insert({ before: content });
35
35
  return false;
@@ -16,13 +16,15 @@ module NestedForm
16
16
  hidden_field(:_destroy) + @template.link_to(name, "javascript:void(0)", :class => "remove_nested_fields")
17
17
  end
18
18
 
19
- def fields_for_with_nested_attributes(association, args, block)
19
+ def fields_for_with_nested_attributes(association_name, args, block)
20
+ # TODO Test this better
21
+ block ||= Proc.new { |fields| @template.render(:partial => "#{association_name.to_s.singularize}_fields", :locals => {:f => fields}) }
20
22
  @fields ||= {}
21
- @fields[association] = block
22
- super
23
+ @fields[association_name] = block
24
+ super(association_name, args, block)
23
25
  end
24
26
 
25
- def fields_for_nested_model(name, association, args, block)
27
+ def fields_for_nested_model(name, object, options, block)
26
28
  output = '<div class="fields">'.html_safe
27
29
  output << super
28
30
  output.safe_concat('</div>')
@@ -9,7 +9,7 @@ module NestedForm
9
9
  end
10
10
  output << fields.join(" ").html_safe
11
11
  end
12
-
12
+
13
13
  def after_nested_form(association, &block)
14
14
  @associations ||= []
15
15
  @after_nested_form_callbacks ||= []
data/spec/spec_helper.rb CHANGED
@@ -22,15 +22,15 @@ end
22
22
 
23
23
  class TablelessModel < ActiveRecord::Base
24
24
  def self.columns() @columns ||= []; end
25
-
25
+
26
26
  def self.column(name, sql_type = nil, default = nil, null = true)
27
27
  columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
28
28
  end
29
-
29
+
30
30
  def self.quoted_table_name
31
31
  name.pluralize.underscore
32
32
  end
33
-
33
+
34
34
  def quoted_id
35
35
  "0"
36
36
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: nested_form
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.0
5
+ version: 0.1.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Ryan Bates
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2011-02-15 00:00:00 -08:00
14
+ date: 2011-03-26 00:00:00 -07:00
15
15
  default_executable:
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
@@ -65,6 +65,7 @@ files:
65
65
  - spec/nested_form/builder_spec.rb
66
66
  - spec/nested_form/view_helper_spec.rb
67
67
  - spec/spec_helper.rb
68
+ - CHANGELOG.rdoc
68
69
  - Gemfile
69
70
  - LICENSE
70
71
  - Rakefile