2011-02-09 21:51:32 +00:00
# cocoon
2011-02-09 21:03:53 +00:00
2011-07-17 16:08:13 +00:00
[![Build Status ](http://travis-ci.org/nathanvda/cocoon.png )](http://travis-ci.org/nathanvda/cocoon)
2011-02-09 21:51:32 +00:00
cocoon is a Rails3 gem to allow easier handling of nested forms.
2011-02-09 21:03:53 +00:00
Nested forms are forms that handle nested models and attributes in one form.
For example a project with its tasks, an invoice with its ordered items.
2011-02-09 21:51:32 +00:00
It is formbuilder-agnostic, so it works with standard Rails, or Formtastic or simple_form.
2011-02-09 21:03:53 +00:00
## Prerequisites
2011-02-09 21:51:32 +00:00
This gem uses jQuery, it is most useful to use this gem in a rails3
project where you are already using jQuery.
Furthermore i would advice you to use either formtastic or simple_form.
2011-02-09 21:03:53 +00:00
2011-02-09 21:51:32 +00:00
I have a sample project where I demonstrate the use of cocoon with formtastic.
2011-02-09 21:03:53 +00:00
## Installation
Inside your `Gemfile` add the following:
2011-12-06 20:15:58 +00:00
````ruby
gem "cocoon"
````
2011-02-09 21:03:53 +00:00
2011-08-19 09:38:15 +00:00
### Rails 3.1
Add the following to `application.js` so it compiles to the
asset_pipeline
2011-12-06 20:15:58 +00:00
````ruby
//= require cocoon
````
2011-08-19 09:38:15 +00:00
### Rails 3.x
2011-07-23 22:57:57 +00:00
If you are using Rails 3.0.x, you need to run the installation task (since rails 3.1 this is no longer needed):
2011-02-09 21:03:53 +00:00
2011-12-06 20:15:58 +00:00
````ruby
rails g cocoon:install
````
2011-02-09 21:03:53 +00:00
This will install the needed javascript file.
Inside your `application.html.haml` you will need to add below the default javascripts:
2011-12-06 20:15:58 +00:00
````haml
= javascript_include_tag :cocoon
````
2011-02-09 21:03:53 +00:00
or using erb, you write
2011-12-06 20:15:58 +00:00
````ruby
< %= javascript_include_tag :cocoon %>
````
2011-02-09 21:03:53 +00:00
That is all you need to do to start using it!
## Usage
Suppose you have a model `Project` :
2011-12-06 20:15:58 +00:00
````ruby
rails g scaffold Project name:string description:string
````
2011-02-09 21:03:53 +00:00
and a project has many `tasks` :
2011-12-06 20:15:58 +00:00
````ruby
rails g model Task description:string done:boolean project_id:integer
````
2011-02-09 21:03:53 +00:00
Edit the models to code the relation:
2011-12-06 20:15:58 +00:00
````ruby
class Project < ActiveRecord::Base
has_many :tasks
2011-12-07 22:48:13 +00:00
accepts_nested_attributes_for :tasks, :reject_if => :all_blank, :allow_destroy => true
2011-12-06 20:15:58 +00:00
end
2011-02-09 21:03:53 +00:00
2011-12-06 20:15:58 +00:00
class Task < ActiveRecord::Base
belongs_to :project
end
````
2011-02-09 21:03:53 +00:00
What we want to achieve is to get a form where we can add and remove the tasks dynamically.
What we need for this, is that the fields for a new/existing `task` are defined in a partial
view called `_task_fields.html` .
2011-02-09 21:51:32 +00:00
We will show the sample usage with the different possible form-builders.
### Using formtastic
2011-02-09 21:03:53 +00:00
Inside our `projects/_form` partial we then write:
2011-12-06 20:15:58 +00:00
````haml
- f.inputs do
= f.input :name
= f.input :description
%h3 Tasks
#tasks
= f.semantic_fields_for :tasks do |task|
= render 'task_fields', :f => task
.links
= link_to_add_association 'add task', f, :tasks
2012-04-20 12:07:43 +00:00
-f.actions do
= f.action :submit
2011-12-06 20:15:58 +00:00
````
2011-02-09 21:03:53 +00:00
and inside the `_task_fields` partial we write:
2011-12-06 20:15:58 +00:00
````haml
.nested-fields
= f.inputs do
= f.input :description
= f.input :done, :as => :boolean
= link_to_remove_association "remove task", f
````
2011-02-09 21:03:53 +00:00
That is all there is to it!
2012-01-05 23:17:06 +00:00
There is an example project on github implementing it called [cocoon_formtastic_demo ](https://github.com/nathanvda/cocoon_formtastic_demo ).
2011-02-09 21:03:53 +00:00
2011-02-09 21:51:32 +00:00
### Using simple_form
2012-07-07 18:19:53 +00:00
Inside our `projects/_form` partial we then write:
2011-02-09 23:19:18 +00:00
2012-07-07 18:19:53 +00:00
````haml
- simple_form_for @project do |f|
= f.input :name
= f.input :description
%h3 Tasks
#tasks
= f.simple_fields_for :tasks do |task|
= render 'task_fields', :f => task
.links
= link_to_add_association 'add task', f, :tasks
= f.submit
````
and inside the `_task_fields` partial we write:
2011-02-09 21:51:32 +00:00
2012-07-07 18:19:53 +00:00
````haml
.nested-fields
= f.input :description
= f.input :done, :as => :boolean
= link_to_remove_association "remove task", f
````
There is an example project on github implementing it called [cocoon_simple_form_demo ](https://github.com/nathanvda/cocoon_simple_form_demo ).
2011-02-09 21:51:32 +00:00
### Using standard rails forms
2011-02-09 23:19:18 +00:00
I will provide a full example (and a sample project) later.
2011-02-09 21:51:32 +00:00
2011-02-09 21:03:53 +00:00
## How it works
I define two helper functions:
2011-12-06 20:15:58 +00:00
### link_to_add_association
2011-02-09 21:03:53 +00:00
This function will add a link to your markup that will, when clicked, dynamically add a new partial form for the given association.
This should be placed below the `semantic_fields_for` .
2011-03-03 20:46:25 +00:00
It takes four parameters:
2011-02-09 21:03:53 +00:00
- name: the text to show in the link
2011-03-03 20:46:25 +00:00
- f: referring to the containing form-object
2011-02-09 21:03:53 +00:00
- association: the name of the association (plural) of which a new instance needs to be added (symbol or string).
2011-03-03 20:46:25 +00:00
- html_options: extra html-options (see `link_to` )
2012-04-06 23:15:27 +00:00
There are some special options, the first three allow to control the placement of the new link-data:
2011-03-03 20:46:25 +00:00
- `data-association-insertion-node` : the jquery selector of the node
2011-12-06 20:03:25 +00:00
- `data-association-insertion-method` : jquery method that inserts the new data. `before` , `after` , `append` , `prepend` , etc. Default: `before`
- `data-association-insertion-position` : old method specifying where to insert new data.
- this setting still works but `data-association-insertion-method` takes precedence. may be removed in a future version.
2012-04-06 23:15:27 +00:00
- `partial` : explicitly declare the name of the partial that will be used
- `render_options` : options passed through to the form-builder function (e.g. `simple_fields_for` , `semantic_fields_for` or `fields_for` ).
2012-04-07 00:14:26 +00:00
If it contains a `:locals` option containing a hash, that is handed to the partial.
2011-03-03 20:46:25 +00:00
Optionally you could also leave out the name and supply a block that is captured to give the name (if you want to do something more complicated).
2011-02-09 21:03:53 +00:00
2012-04-06 23:15:27 +00:00
#### :render_options
Inside the `html_options` you can add an option `:render_options` , and the containing hash will be handed down to the form-builder for the inserted
form. E.g. especially when using `twitter-bootstrap` and `simple_form` together, the `simple_fields_for` needs the option `:wrapper => 'inline'` which can
be handed down as follows:
2012-07-06 18:04:44 +00:00
(Note: In certain newer versions of simple_form, the option to use is ':wrapper => 'bootstrap')
2012-04-06 23:15:27 +00:00
````haml
= link_to_add_association 'add something', f, :something, :render_options => {:wrapper => 'inline' }
````
2012-04-07 00:14:26 +00:00
If you want to specify locals that needed to handed down to the partial, write
````haml
= link_to_add_association 'add something', f, :something, :render_options => {:locals => {:sherlock => 'Holmes' }}
````
2012-04-06 23:15:27 +00:00
#### :partial
To overrule the default partial name, e.g. because it shared between multiple views, write
````haml
= link_to_add_association 'add something', f, :something, :partial => 'shared/something_fields'
````
2011-04-24 22:58:37 +00:00
2011-12-06 20:15:58 +00:00
### link_to_remove_association
2011-02-09 21:03:53 +00:00
This function will add a link to your markup that will, when clicked, dynamically remove the surrounding partial form.
This should be placed inside the partial `_<association-object-singular>_fields` .
2011-03-03 20:46:25 +00:00
It takes three parameters:
- name: the text to show in the link
- f: referring to the containing form-object
- html_options: extra html-options (see `link_to` )
Optionally you could also leave out the name and supply a block that is captured to give the name (if you want to do something more complicated).
2011-10-22 21:00:51 +00:00
2011-10-12 20:59:23 +00:00
### Callbacks (upon insert and remove of items)
2011-12-06 20:03:25 +00:00
There is an option to add a callback on insertion or removal. If in your view you have the following snippet to select an `owner`
2011-10-12 20:59:23 +00:00
(we use slim for demonstration purposes)
2011-12-06 20:15:58 +00:00
````haml
#owner
#owner_from_list
= f.association :owner, :collection => Person.all(:order => 'name'), :prompt => 'Choose an existing owner'
= link_to_add_association 'add a new person as owner', f, :owner
````
2011-10-12 20:59:23 +00:00
This view part will either let you select an owner from the list of persons, or show the fields to add a new person as owner.
The callbacks can be added as follows:
2011-12-06 20:15:58 +00:00
````javascript
$(document).ready(function() {
$('#owner').bind('insertion-callback',
function() {
$("#owner_from_list").hide();
$("#owner a.add_fields").hide();
});
$('#owner').bind("removal-callback",
function() {
$("#owner_from_list").show();
$("#owner a.add_fields").show();
});
2012-04-06 23:33:30 +00:00
$('#owner').bind("after-removal-callback",
function() {
/* e.g. recalculate order of child items */
});
2011-12-06 20:15:58 +00:00
});
````
2011-10-12 20:59:23 +00:00
Do note that for the callbacks to work there has to be a surrounding container (div), where you can bind the callbacks to.
2012-04-06 23:33:30 +00:00
Note that the default `removal-callback` is called _before_ removing the nested item.
2011-10-12 20:59:23 +00:00
### Control the Insertion behaviour
2011-12-06 20:03:25 +00:00
The default insertion location is at the back of the current container. But we have added two `data` -attributes that are read to determine the insertion-node and -method.
2011-10-12 20:59:23 +00:00
For example:
2011-12-06 20:15:58 +00:00
````javascript
$(document).ready(function() {
$("#owner a.add_fields").
data("association-insertion-method", 'before').
data("association-insertion-node", 'this');
});
````
2011-10-12 20:59:23 +00:00
The `association-insertion-node` will determine where to add it. You can choose any selector here, or specify this (default it is the parent-container).
2011-12-06 20:03:25 +00:00
The `association-insertion-method` will determine where to add it in relation with the node. Any jQuery DOM Manipulation method can be set but we recommend sticking to any of the following: `before` , `after` , `append` , `prepend` . It is unknown at this time what others would do.
2011-10-12 20:59:23 +00:00
2011-02-09 21:03:53 +00:00
### Partial
2012-04-06 23:15:27 +00:00
If no explicit partial-name is given, `cocoon` looks for a file named `_<association-object_singular>_fields` .
To override the default partial-name use the option `:partial` .
For the javascript to behave correctly, the partial should start with a container (e.g. `div` ) of class `.nested-fields` .
2011-02-09 21:03:53 +00:00
There is no limit to the amount of nesting, though.
## Note on Patches/Pull Requests
* Fork the project.
* Make your feature addition or bug fix.
* Add tests for it. This is important so I don't break it in a
future version unintentionally.
* Commit, do not mess with rakefile, version, or history.
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
* Send me a pull request. Bonus points for topic branches.
2011-02-09 21:51:32 +00:00
## Todo
2011-12-06 20:03:25 +00:00
* add more sample relations: `has_many :through` , `belongs_to` , ...
2011-03-15 15:16:02 +00:00
* improve the tests (test the javascript too)(if anybody wants to lend a hand ...?)
2011-02-09 21:51:32 +00:00
2011-02-09 21:03:53 +00:00
## Copyright
2011-02-09 21:51:32 +00:00
Copyright (c) 2010 Nathan Van der Auwera. See LICENSE for details.