awesome_nested_fields 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile.lock +78 -0
- data/LICENSE +20 -0
- data/README.md +102 -0
- data/app/helpers/awesome_nested_fields_helper.rb +11 -4
- data/lib/awesome_nested_fields/version.rb +1 -1
- data/public/javascripts/jquery.nested-fields.js +12 -9
- metadata +7 -27
data/.gitignore
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
awesome_nested_fields (0.0.3)
|
5
|
+
rails (>= 3.0.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
abstract (1.0.0)
|
11
|
+
actionmailer (3.0.4)
|
12
|
+
actionpack (= 3.0.4)
|
13
|
+
mail (~> 2.2.15)
|
14
|
+
actionpack (3.0.4)
|
15
|
+
activemodel (= 3.0.4)
|
16
|
+
activesupport (= 3.0.4)
|
17
|
+
builder (~> 2.1.2)
|
18
|
+
erubis (~> 2.6.6)
|
19
|
+
i18n (~> 0.4)
|
20
|
+
rack (~> 1.2.1)
|
21
|
+
rack-mount (~> 0.6.13)
|
22
|
+
rack-test (~> 0.5.7)
|
23
|
+
tzinfo (~> 0.3.23)
|
24
|
+
activemodel (3.0.4)
|
25
|
+
activesupport (= 3.0.4)
|
26
|
+
builder (~> 2.1.2)
|
27
|
+
i18n (~> 0.4)
|
28
|
+
activerecord (3.0.4)
|
29
|
+
activemodel (= 3.0.4)
|
30
|
+
activesupport (= 3.0.4)
|
31
|
+
arel (~> 2.0.2)
|
32
|
+
tzinfo (~> 0.3.23)
|
33
|
+
activeresource (3.0.4)
|
34
|
+
activemodel (= 3.0.4)
|
35
|
+
activesupport (= 3.0.4)
|
36
|
+
activesupport (3.0.4)
|
37
|
+
arel (2.0.8)
|
38
|
+
builder (2.1.2)
|
39
|
+
erubis (2.6.6)
|
40
|
+
abstract (>= 1.0.0)
|
41
|
+
i18n (0.5.0)
|
42
|
+
mail (2.2.15)
|
43
|
+
activesupport (>= 2.3.6)
|
44
|
+
i18n (>= 0.4.0)
|
45
|
+
mime-types (~> 1.16)
|
46
|
+
treetop (~> 1.4.8)
|
47
|
+
mime-types (1.16)
|
48
|
+
polyglot (0.3.1)
|
49
|
+
rack (1.2.1)
|
50
|
+
rack-mount (0.6.13)
|
51
|
+
rack (>= 1.0.0)
|
52
|
+
rack-test (0.5.7)
|
53
|
+
rack (>= 1.0)
|
54
|
+
rails (3.0.4)
|
55
|
+
actionmailer (= 3.0.4)
|
56
|
+
actionpack (= 3.0.4)
|
57
|
+
activerecord (= 3.0.4)
|
58
|
+
activeresource (= 3.0.4)
|
59
|
+
activesupport (= 3.0.4)
|
60
|
+
bundler (~> 1.0)
|
61
|
+
railties (= 3.0.4)
|
62
|
+
railties (3.0.4)
|
63
|
+
actionpack (= 3.0.4)
|
64
|
+
activesupport (= 3.0.4)
|
65
|
+
rake (>= 0.8.7)
|
66
|
+
thor (~> 0.14.4)
|
67
|
+
rake (0.8.7)
|
68
|
+
thor (0.14.6)
|
69
|
+
treetop (1.4.9)
|
70
|
+
polyglot (>= 0.3.1)
|
71
|
+
tzinfo (0.3.24)
|
72
|
+
|
73
|
+
PLATFORMS
|
74
|
+
ruby
|
75
|
+
|
76
|
+
DEPENDENCIES
|
77
|
+
awesome_nested_fields!
|
78
|
+
bundler (>= 1.0.0)
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Lailson Bandeira
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
Awesome Nested Fields
|
2
|
+
=====================
|
3
|
+
|
4
|
+
In Rails, you can create forms that have fields from nested models. For example, if a person has many phone numbers, you can easily create a form that receives data from the person and from a fixed number of phones. However, when you want to allow the person to insert multiple, indefinite phones, you're in trouble: it's [much harder](http://railscasts.com/episodes/196-nested-model-form-part-1) [than it](http://railscasts.com/episodes/197-nested-model-form-part-2) [should be](http://stackoverflow.com/questions/1704142/unobtrusive-dynamic-form-fields-in-rails-with-jquery). Well, not anymore.
|
5
|
+
|
6
|
+
|
7
|
+
Installation
|
8
|
+
------------
|
9
|
+
|
10
|
+
1. Add the gem to your Gemfile.
|
11
|
+
|
12
|
+
gem 'awesome_nested_fields'
|
13
|
+
|
14
|
+
2. Run bundler to make sure the gem gets installed.
|
15
|
+
|
16
|
+
bundle install
|
17
|
+
|
18
|
+
3. Include the `jquery.nested-fields.js` file in your template (or in the pages that will use nested fields).
|
19
|
+
|
20
|
+
<script src="/javascripts/jquery.nested-fields.js" type="text/javascript"></script>
|
21
|
+
|
22
|
+
Now you're ready to rock with nested models. Don't forget to include the javascript file _after_ you've included jQuery. And don't worry because this file isn't on the public folder: it comes bundled into the gem.
|
23
|
+
|
24
|
+
|
25
|
+
Basic Usage
|
26
|
+
-----------
|
27
|
+
|
28
|
+
### Model
|
29
|
+
|
30
|
+
First, make sure the object that has the `has_many` or `has_and_belongs_to_many` relation accepts nested attributes for the collection you want. For example, if a person _has_many_ phones, we'll have a model like this:
|
31
|
+
|
32
|
+
class Person < ActiveRecord::Base
|
33
|
+
has_many :phones
|
34
|
+
accepts_nested_attributes_for :phones, allow_destroy: true
|
35
|
+
end
|
36
|
+
|
37
|
+
The `accepts_nested_attributes_for` is a method from Active Record that allows you to pass attributes of nested models directly to its parent, instead of instantiate each child object separately. In this case, `Person` gains a method called `phones_attributes=`, that accepts data for new and existing phones of a given person. The `allow_destroy` option enables us to also delete child objects. To know more about nested attributes, check out the [ActiveRecord::NestedAttribute](https://github.com/rails/rails/blob/master/activerecord/lib/active_record/nested_attributes.rb#L1) class.
|
38
|
+
|
39
|
+
### View
|
40
|
+
|
41
|
+
The next step is set up the form view using the `nested_fields` helper method. It receives three parameters: the parent form builder, the association name and an optional hash of options (humm, a pun).
|
42
|
+
Proceeding with the person/phones example, we can have a form like this:
|
43
|
+
|
44
|
+
<%= form_for(@person) do |f| %>
|
45
|
+
<% # person fields... %>
|
46
|
+
|
47
|
+
<h2>Phones</h2>
|
48
|
+
<div class="container">
|
49
|
+
<%= nested_fields(f, :phones) %>
|
50
|
+
</div>
|
51
|
+
<a href="#" class="add">add phone</a>
|
52
|
+
|
53
|
+
<% # more person fields... %>
|
54
|
+
<% end %>
|
55
|
+
|
56
|
+
The `nested_fields` helper lists the phones this person has and also adds an empty template to the page for creating new phones. But where is the phone form? Well, awesome_nested_fields expects a partial with the association name in the singular (after all, the partial represents a single child object). In this case, it looks for the partial `phone` (we can change this name later). So, in the file `_phone.html.erb`, we can have:
|
57
|
+
|
58
|
+
<fieldset class="item">
|
59
|
+
<%= f.label :where %>
|
60
|
+
<%= f.text_field :where %><br/>
|
61
|
+
|
62
|
+
<%= f.label :number %>
|
63
|
+
<%= f.text_field :number %>
|
64
|
+
|
65
|
+
<a href="#" class="remove">remove</a>
|
66
|
+
|
67
|
+
<%= f.hidden_field :id %>
|
68
|
+
<%= f.hidden_field :_destroy %>
|
69
|
+
</fieldset>
|
70
|
+
|
71
|
+
If you're paying attention, you noticed the key elements are marked with a special class name. We need this for the javascript code, so it knows what to do with each HTML element: the one that have the children must have the class `container`; each child must be marked with the class `item`; inside an item, the link for removal must have the class `remove`; and the link to add new items must have the class `add`. We can change the names later, but these are the default choices. Finally, don't forget to add the `id` field, as it is needed by AR to identify if this is an existing or a new element, and the `_destroy` field to activate deletion when the user clicks on the remove link.
|
72
|
+
|
73
|
+
### Javascript
|
74
|
+
|
75
|
+
This is the easiest part: just activate the nested fields actions when the page loads. We can put this in the `application.js` file (or in any other place that gets executed in the page):
|
76
|
+
|
77
|
+
$(document).ready(function(e) {
|
78
|
+
$('FORM').nestedFields();
|
79
|
+
});
|
80
|
+
|
81
|
+
Now enjoy your new nested model form!
|
82
|
+
|
83
|
+
|
84
|
+
Compatibility
|
85
|
+
-------------
|
86
|
+
|
87
|
+
awesome_nested_fields works only with Rails 3 and was tested with jQuery 1.5.0.
|
88
|
+
|
89
|
+
|
90
|
+
TODO
|
91
|
+
----
|
92
|
+
|
93
|
+
* Write tests
|
94
|
+
* Write awesome demos
|
95
|
+
* Make sure it can degrade gracefully
|
96
|
+
* Return and API object on JS to make interaction easier
|
97
|
+
* Port JavaScript code to CoffeeScript
|
98
|
+
|
99
|
+
|
100
|
+
Copyleft
|
101
|
+
--------
|
102
|
+
Copyleft (c) 2011 Lailson Bandeira (http://lailsonbandeira.com/). See LICENSE for details.
|
@@ -1,4 +1,10 @@
|
|
1
1
|
module AwesomeNestedFieldsHelper
|
2
|
+
|
3
|
+
def nested_fields(builder, association, options={})
|
4
|
+
nested_fields_items(builder, association, options) <<
|
5
|
+
nested_fields_template(builder, association, options)
|
6
|
+
end
|
7
|
+
|
2
8
|
def nested_fields_items(builder, association, options={})
|
3
9
|
options = nested_fields_process_default_options(options, builder, association)
|
4
10
|
|
@@ -8,7 +14,7 @@ module AwesomeNestedFieldsHelper
|
|
8
14
|
end
|
9
15
|
|
10
16
|
if options[:none_partial] and builder.object.send(association).empty?
|
11
|
-
items << render(options[:none_partial]
|
17
|
+
items << render(options[:none_partial])
|
12
18
|
end
|
13
19
|
|
14
20
|
items.html_safe
|
@@ -18,14 +24,14 @@ module AwesomeNestedFieldsHelper
|
|
18
24
|
options = nested_fields_process_default_options(options, builder, association)
|
19
25
|
|
20
26
|
templates = content_tag(:script, type: 'text/html', class: options[:item_template_class]) do
|
21
|
-
builder.fields_for(association, options[:new_object], child_index:
|
27
|
+
builder.fields_for(association, options[:new_object], child_index: options[:new_item_index]) do |f|
|
22
28
|
render(options[:partial], options[:builder_local] => f)
|
23
29
|
end
|
24
30
|
end
|
25
31
|
|
26
32
|
if options[:none_partial]
|
27
33
|
templates << content_tag(:script, type: 'text/html', class: options[:none_template_class]) do
|
28
|
-
builder.fields_for(association, options[:new_object], child_index:
|
34
|
+
builder.fields_for(association, options[:new_object], child_index: options[:new_item_index]) do |f|
|
29
35
|
render(options[:none_partial], options[:builder_local] => f)
|
30
36
|
end
|
31
37
|
end
|
@@ -38,9 +44,10 @@ protected
|
|
38
44
|
def nested_fields_process_default_options(options, builder, association)
|
39
45
|
options[:new_object] ||= builder.object.class.reflect_on_association(association).klass.new
|
40
46
|
options[:partial] ||= association.to_s.singularize
|
41
|
-
options[:builder_local] ||= :
|
47
|
+
options[:builder_local] ||= :f
|
42
48
|
options[:item_template_class] ||= 'template item'
|
43
49
|
options[:none_template_class] ||= 'template none'
|
50
|
+
options[:new_item_index] ||= 'new_nested_item'
|
44
51
|
options
|
45
52
|
end
|
46
53
|
end
|
@@ -10,8 +10,8 @@
|
|
10
10
|
container: '.container',
|
11
11
|
item: '.item',
|
12
12
|
none: '.none',
|
13
|
-
|
14
|
-
|
13
|
+
add: '.add',
|
14
|
+
remove: '.remove',
|
15
15
|
newItemIndex: 'new_nested_item'
|
16
16
|
};
|
17
17
|
|
@@ -31,10 +31,10 @@
|
|
31
31
|
options.itemTemplate = $(options.itemTemplate, $this);
|
32
32
|
options.noneTemplate = $(options.noneTemplate, $this);
|
33
33
|
options.container = $(options.container, $this);
|
34
|
-
options.
|
34
|
+
options.add = $(options.add, $this);
|
35
35
|
$this.data('nested-fields.options', options);
|
36
36
|
|
37
|
-
options.
|
37
|
+
options.add.bind('click.nested-fields', function(e) {
|
38
38
|
e.preventDefault();
|
39
39
|
var newItem = prepareTemplate(options);
|
40
40
|
insertItemWithCallbacks(newItem, null, options);
|
@@ -165,17 +165,20 @@
|
|
165
165
|
}
|
166
166
|
|
167
167
|
function bindRemoveEvent(item, options) {
|
168
|
-
var removeHandler = $(item).find(options.
|
168
|
+
var removeHandler = $(item).find(options.remove);
|
169
169
|
var needsConfirmation = removeHandler.attr('data-confirm');
|
170
170
|
|
171
|
-
var event = needsConfirmation ? '
|
172
|
-
removeHandler.bind(event + '.nested-fields', function(e) {
|
173
|
-
|
171
|
+
var event = needsConfirmation ? 'confirm:complete' : 'click';
|
172
|
+
removeHandler.bind(event + '.nested-fields', function(e, confirmed) {
|
173
|
+
e.preventDefault();
|
174
|
+
if(confirmed === undefined || confirmed === true) {
|
175
|
+
removeItem(item, options);
|
176
|
+
}
|
174
177
|
});
|
175
178
|
}
|
176
179
|
|
177
180
|
function insertNone(options) {
|
178
|
-
if(findItems(options).length
|
181
|
+
if(findItems(options).length === 0) {
|
179
182
|
options.container.append(options.noneTemplate.html());
|
180
183
|
}
|
181
184
|
}
|
metadata
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: awesome_nested_fields
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 3
|
10
|
-
version: 0.0.3
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
11
6
|
platform: ruby
|
12
7
|
authors:
|
13
8
|
- Lailson Bandeira
|
@@ -15,7 +10,7 @@ autorequire:
|
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
12
|
|
18
|
-
date: 2011-
|
13
|
+
date: 2011-06-06 00:00:00 -03:00
|
19
14
|
default_executable:
|
20
15
|
dependencies:
|
21
16
|
- !ruby/object:Gem::Dependency
|
@@ -26,11 +21,6 @@ dependencies:
|
|
26
21
|
requirements:
|
27
22
|
- - ">="
|
28
23
|
- !ruby/object:Gem::Version
|
29
|
-
hash: 23
|
30
|
-
segments:
|
31
|
-
- 1
|
32
|
-
- 0
|
33
|
-
- 0
|
34
24
|
version: 1.0.0
|
35
25
|
type: :development
|
36
26
|
version_requirements: *id001
|
@@ -42,11 +32,6 @@ dependencies:
|
|
42
32
|
requirements:
|
43
33
|
- - ">="
|
44
34
|
- !ruby/object:Gem::Version
|
45
|
-
hash: 7
|
46
|
-
segments:
|
47
|
-
- 3
|
48
|
-
- 0
|
49
|
-
- 0
|
50
35
|
version: 3.0.0
|
51
36
|
type: :runtime
|
52
37
|
version_requirements: *id002
|
@@ -61,6 +46,9 @@ extra_rdoc_files: []
|
|
61
46
|
files:
|
62
47
|
- .gitignore
|
63
48
|
- Gemfile
|
49
|
+
- Gemfile.lock
|
50
|
+
- LICENSE
|
51
|
+
- README.md
|
64
52
|
- Rakefile
|
65
53
|
- app/helpers/awesome_nested_fields_helper.rb
|
66
54
|
- awesome_nested_fields.gemspec
|
@@ -81,25 +69,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
81
69
|
requirements:
|
82
70
|
- - ">="
|
83
71
|
- !ruby/object:Gem::Version
|
84
|
-
hash: 3
|
85
|
-
segments:
|
86
|
-
- 0
|
87
72
|
version: "0"
|
88
73
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
74
|
none: false
|
90
75
|
requirements:
|
91
76
|
- - ">="
|
92
77
|
- !ruby/object:Gem::Version
|
93
|
-
hash: 23
|
94
|
-
segments:
|
95
|
-
- 1
|
96
|
-
- 3
|
97
|
-
- 6
|
98
78
|
version: 1.3.6
|
99
79
|
requirements: []
|
100
80
|
|
101
81
|
rubyforge_project: awesome_nested_fields
|
102
|
-
rubygems_version: 1.
|
82
|
+
rubygems_version: 1.6.2
|
103
83
|
signing_key:
|
104
84
|
specification_version: 3
|
105
85
|
summary: Awesome nested fields for Rails
|