add support for sorting nested forms
This commit is contained in:
parent
f584e181d2
commit
bfe72bd29f
|
@ -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
|
This gem uses jQuery, it is most useful to use this gem in a rails3
|
||||||
project where you are already using jQuery.
|
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.
|
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.
|
I have a sample project where I demonstrate the use of cocoon with formtastic.
|
||||||
|
@ -37,6 +39,13 @@ asset_pipeline
|
||||||
//= require cocoon
|
//= 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
|
### 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):
|
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).
|
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
|
### Using simple_form
|
||||||
|
|
||||||
Inside our `projects/_form` partial we then write:
|
Inside our `projects/_form` partial we then write:
|
||||||
|
|
|
@ -51,14 +51,14 @@
|
||||||
|
|
||||||
var contentNode = $(new_content);
|
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)
|
// 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
|
// 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'
|
// code and doesn't force it to be a sibling like after/before does. default: 'before'
|
||||||
insertionNode[insertionMethod](contentNode);
|
insertionNode[insertionMethod](contentNode);
|
||||||
|
|
||||||
insertionNode.trigger('cocoon:after-insert');
|
insertionNode.trigger('cocoon:after-insert', contentNode);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.remove_fields.dynamic').live('click', function(e) {
|
$('.remove_fields.dynamic').live('click', function(e) {
|
||||||
|
|
|
@ -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);
|
|
@ -18,5 +18,12 @@ li.cocoon {
|
||||||
.links a {
|
.links a {
|
||||||
@extend .button;
|
@extend .button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.ui-sortable {
|
||||||
|
fieldset {
|
||||||
|
border-top: solid #777 12px;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,12 @@ class CocoonInput
|
||||||
template.link_to_add_association template.t('.add'), builder, method
|
template.link_to_add_association template.t('.add'), builder, method
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue