_updateFields js should start with first list item position of 1, not 0 #1
@ -272,6 +272,7 @@ It takes four parameters:
|
|||||||
- `partial`: explicitly declare the name of the partial that will be used
|
- `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`).
|
- `render_options` : options passed through to the form-builder function (e.g. `simple_fields_for`, `semantic_fields_for` or `fields_for`).
|
||||||
If it contains a `:locals` option containing a hash, that is handed to the partial.
|
If it contains a `:locals` option containing a hash, that is handed to the partial.
|
||||||
|
- `wrap_object` : a proc that will allow to wrap your object, especially useful if you are using decorators (e.g. draper). See example lower.
|
||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
@ -301,6 +302,49 @@ To overrule the default partial name, e.g. because it shared between multiple vi
|
|||||||
= link_to_add_association 'add something', f, :something, :partial => 'shared/something_fields'
|
= link_to_add_association 'add something', f, :something, :partial => 'shared/something_fields'
|
||||||
````
|
````
|
||||||
|
|
||||||
|
#### :wrap_object
|
||||||
|
|
||||||
|
If you are using decorators, the normal instantiation of the associated will not be enough, actually you want to generate the decorated object.
|
||||||
|
|
||||||
|
A simple decorator would look like:
|
||||||
|
|
||||||
|
```
|
||||||
|
class CommentDecorator
|
||||||
|
def initialize(comment)
|
||||||
|
@comment = comment
|
||||||
|
end
|
||||||
|
|
||||||
|
def formatted_created_at
|
||||||
|
@comment.created_at.to_formatted_s(:short)
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(method_sym, *args)
|
||||||
|
if @comment.respond_to?(method_sym)
|
||||||
|
@comment.send(method_sym, *args)
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
To use this, write
|
||||||
|
|
||||||
|
```
|
||||||
|
link_to_add_association('add something', @form_obj, :comments, :wrap_object => Proc.new {|comment| CommentDecorator.new(comment) })
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the `:wrap_object` expects an object that is _callable_, so any `Proc` will do. So you could as well use it to do some fancy extra initialisation (if needed).
|
||||||
|
But note you will have to return the (nested) object you want used.
|
||||||
|
E.g.
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
link_to_add_association('add something', @form_obj, :comments,
|
||||||
|
:wrap_object => Proc.new { |comment| comment.name = current_user.name; comment })
|
||||||
|
```
|
||||||
|
|
||||||
|
> A cleaner option would be to call a function that performs this initialisation and returns `self` at the end.
|
||||||
|
|
||||||
### link_to_remove_association
|
### link_to_remove_association
|
||||||
|
|
||||||
@ -325,6 +369,16 @@ On insertion or removal the following events are triggered:
|
|||||||
* `cocoon:before-remove`: called before removing the nested child
|
* `cocoon:before-remove`: called before removing the nested child
|
||||||
* `cocoon:after-remove`: called after removal
|
* `cocoon:after-remove`: called after removal
|
||||||
|
|
||||||
|
To listen to the events, you to have the following code in your javascript:
|
||||||
|
|
||||||
|
$('#container').bind('cocoon:before-insert', function(e, inserted_item) {
|
||||||
|
// ... do something
|
||||||
|
});
|
||||||
|
|
||||||
|
where `e` is the event and the second parameter is the inserted or removed item. This allows you to change markup, or
|
||||||
|
add effects/animations (see example below).
|
||||||
|
|
||||||
|
|
||||||
If in your view you have the following snippet to select an `owner`
|
If in your view you have the following snippet to select an `owner`
|
||||||
(we use slim for demonstration purposes)
|
(we use slim for demonstration purposes)
|
||||||
|
|
||||||
@ -360,12 +414,38 @@ $(document).ready(function() {
|
|||||||
function() {
|
function() {
|
||||||
/* e.g. recalculate order of child items */
|
/* e.g. recalculate order of child items */
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// example showing manipulating the inserted/removed item
|
||||||
|
|
||||||
|
$('#tasks').bind('cocoon:before-insert', function(e,task_to_be_added) {
|
||||||
|
task_to_be_added.fadeIn('slow');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#tasks').bind('cocoon:after-insert', function(e, added_task) {
|
||||||
|
// e.g. set the background of inserted task
|
||||||
|
added_task.css("background","red");
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#tasks').bind('cocoon:before-remove', function(e, task) {
|
||||||
|
// allow some time for the animation to complete
|
||||||
|
$(this).data('remove-timeout', 1000);
|
||||||
|
task.fadeOut('slow');
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
````
|
````
|
||||||
|
|
||||||
Do note that for the callbacks to work there has to be a surrounding container (div), where you can bind the callbacks to.
|
Do note that for the callbacks to work there has to be a surrounding container (div), where you can bind the callbacks to.
|
||||||
|
|
||||||
|
|
||||||
|
When adding animations and effects to make the removal of items more interesting, you will also have to provide a timeout.
|
||||||
|
This is accomplished by the following line:
|
||||||
|
|
||||||
|
$(this).data('remove-timeout', 1000);
|
||||||
|
|
||||||
|
Note that you could also immediately add this to your view (on the `.nested-fields` container).
|
||||||
|
|
||||||
### Control the Insertion behaviour
|
### Control the Insertion behaviour
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
@ -7,13 +7,6 @@
|
|||||||
content.replace(reg_exp, with_str);
|
content.replace(reg_exp, with_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
function trigger_before_removal_callback(node) {
|
|
||||||
node.trigger('cocoon:before-remove');
|
|
||||||
}
|
|
||||||
|
|
||||||
function trigger_after_removal_callback(node) {
|
|
||||||
node.trigger('cocoon:after-remove');
|
|
||||||
}
|
|
||||||
|
|
||||||
$('.add_fields').live('click', function(e) {
|
$('.add_fields').live('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -56,28 +49,33 @@
|
|||||||
// 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);
|
var addedContent = insertionNode[insertionMethod](contentNode);
|
||||||
|
|
||||||
insertionNode.trigger('cocoon:after-insert', contentNode);
|
insertionNode.trigger('cocoon:after-insert', [contentNode]);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.remove_fields.dynamic').live('click', function(e) {
|
|
||||||
var $this = $(this);
|
|
||||||
var trigger_node = $this.closest(".nested-fields").parent();
|
|
||||||
trigger_before_removal_callback(trigger_node);
|
|
||||||
e.preventDefault();
|
|
||||||
$this.closest(".nested-fields").remove();
|
|
||||||
trigger_after_removal_callback(trigger_node);
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.remove_fields.existing').live('click', function(e) {
|
$('.remove_fields.dynamic, .remove_fields.existing').live('click', function(e) {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var trigger_node = $this.closest(".nested-fields").parent();
|
var node_to_delete = $this.closest(".nested-fields");
|
||||||
trigger_before_removal_callback(trigger_node);
|
var trigger_node = node_to_delete.parent();
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$this.prev("input[type=hidden]").val("1");
|
|
||||||
$this.closest(".nested-fields").hide();
|
trigger_node.trigger('cocoon:before-remove', [node_to_delete]);
|
||||||
trigger_after_removal_callback(trigger_node);
|
|
||||||
|
|
||||||
|
var timeout = trigger_node.data('remove-timeout') || 0;
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
if ($this.hasClass('dynamic')) {
|
||||||
|
$this.closest(".nested-fields").remove();
|
||||||
|
} else {
|
||||||
|
$this.prev("input[type=hidden]").val("1");
|
||||||
|
$this.closest(".nested-fields").hide();
|
||||||
|
}
|
||||||
|
trigger_node.trigger('cocoon:after-remove', [node_to_delete]);
|
||||||
|
}, timeout);
|
||||||
});
|
});
|
||||||
|
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
Loading…
Reference in New Issue
Block a user