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 +16 -0
- data/README.rdoc +20 -19
- data/lib/generators/nested_form/install_generator.rb +1 -1
- data/lib/generators/nested_form/templates/jquery_nested_form.js +39 -37
- data/lib/generators/nested_form/templates/prototype_nested_form.js +3 -3
- data/lib/nested_form/builder.rb +6 -4
- data/lib/nested_form/view_helper.rb +1 -1
- data/spec/spec_helper.rb +3 -3
- metadata +3 -2
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
|
-
|
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
|
-
|
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
|
-
==
|
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
|
-
|
15
|
-
|
16
|
-
bundle install
|
12
|
+
gem "nested_form"
|
17
13
|
|
18
|
-
|
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
|
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
|
-
|
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
|
-
|
23
|
+
== Usage
|
28
24
|
|
29
|
-
|
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
|
-
|
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
|
-
|
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.
|
@@ -1,46 +1,48 @@
|
|
1
|
-
|
2
|
-
$('form a.add_nested_fields').live('click', function() {
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
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 = ($(this).closest('.fields').find('input:first').attr('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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
35
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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][
|
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;
|
data/lib/nested_form/builder.rb
CHANGED
@@ -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(
|
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[
|
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,
|
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>')
|
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.
|
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-
|
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
|