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
|
||||
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:
|
||||
|
@ -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) {
|
||||
|
29
app/assets/javascripts/cocoon/ordered.js
Normal file
29
app/assets/javascripts/cocoon/ordered.js
Normal 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);
|
@ -18,5 +18,12 @@ li.cocoon {
|
||||
.links a {
|
||||
@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
|
||||
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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user