add support for sorting nested forms

This commit is contained in:
John Bintz 2012-10-25 14:49:55 -04:00
parent f584e181d2
commit bfe72bd29f
5 changed files with 100 additions and 3 deletions

View File

@ -16,6 +16,8 @@ This project is not related to [Apache Cocoon](http://cocoon.apache.org/)
This gem uses jQuery, it is most useful to use this gem in a rails3
project where you are already using jQuery.
Sortable form support requires jQuery UI.
Furthermore i would advice you to use either formtastic or simple_form.
I have a sample project where I demonstrate the use of cocoon with formtastic.
@ -37,6 +39,13 @@ asset_pipeline
//= require cocoon
````
If you also want to be able to sort nested forms, ordering them on a particular field, add `cocoon/ordered`:
``` ruby
//= require cocoon
//= require cocoon/ordered
```
### Rails 3.0.x
If you are using Rails 3.0.x, you need to run the installation task (since rails 3.1 this is no longer needed):
@ -125,6 +134,53 @@ That is all there is to it!
There is an example project on github implementing it called [cocoon_formtastic_demo](https://github.com/nathanvda/cocoon_formtastic_demo).
Or, you can use the Formtastic `cocoon` field type to wrap up much of the boilerplate of the wrapper and
add association button:
``` haml
= f.inputs do
= f.input :name
= f.input :description
%h3 Tasks
#tasks
= f.input :tasks, :as => :cocoon
= f.actions do
= f.action :submit
```
#### Sortable forms
Say you have a set of nested models that are ordered arbitrarily:
``` ruby
class Task < ActiveRecord::Base
belongs_to :project
default_scope :order => 'order ASC'
end
```
You want users to be able to sort those
models via the UI. You can do this by including `cocoon/ordered` and specifying the sort field in the Formtastic
input call:
``` haml
= f.input :tasks, :as => :cocoon, :ordered_by => :order
```
Add the order field as a hidden field in the nested form:
``` haml
.nested-fields
= f.inputs do
= f.input :description
= f.input :done, :as => :boolean
= f.input :order, :as => :hidden
= link_to_remove_association "remove task", f
```
The order field will now be filled in correctly when new models are added and when the models are sorted.
### Using simple_form
Inside our `projects/_form` partial we then write:

View File

@ -51,14 +51,14 @@
var contentNode = $(new_content);
insertionNode.trigger('cocoon:before-insert');
insertionNode.trigger('cocoon:before-insert', contentNode);
// allow any of the jquery dom manipulation methods (after, before, append, prepend, etc)
// to be called on the node. allows the insertion node to be the parent of the inserted
// code and doesn't force it to be a sibling like after/before does. default: 'before'
insertionNode[insertionMethod](contentNode);
insertionNode.trigger('cocoon:after-insert');
insertionNode.trigger('cocoon:after-insert', contentNode);
});
$('.remove_fields.dynamic').live('click', function(e) {

View File

@ -0,0 +1,29 @@
//= require jquery-ui
//
(function($) {
$(function() {
$('li[data-ordered_by]').each(function(index, element) {
var field = $(element).data('ordered_by');
var fieldSearch = "[name*='[" + field + "]']"
$(element).sortable({
items: '.nested-fields',
stop: function(e, ui) {
$(element).find(fieldSearch).each(function(index, element) {
$(element).val(index);
});
}
});
$(element).bind('cocoon:after-insert', function(e, node) {
var nextOrder = 0;
$(element).find(fieldSearch).each(function() {
nextOrder = Math.max(nextOrder, Number($(this).val()) + 1);
});
$(node).find(fieldSearch).val(nextOrder)
});
});
});
})(jQuery);

View File

@ -18,5 +18,12 @@ li.cocoon {
.links a {
@extend .button;
}
&.ui-sortable {
fieldset {
border-top: solid #777 12px;
cursor: move;
}
}
}

View File

@ -18,7 +18,12 @@ class CocoonInput
template.link_to_add_association template.t('.add'), builder, method
end
template.content_tag(:li, output.join('').html_safe, :class => 'input cocoon')
data = { :class => 'input cocoon' }
if options[:ordered_by]
data['data-ordered_by'] = options[:ordered_by]
end
template.content_tag(:li, output.join('').html_safe, data)
end
end