nested_form 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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