A jQuery plugin that manages adding, deleting and moving elements from a Symfony2 collection
This is not really difficult to manage your collections using the data-prototype Symfony2 provides. But
after using several times collections, it appeared useful to me to create a jQuery plugin to do this job.
This is even more true when you need your elements to be moved up and down or added at a specific position: as the form will be proceeded using field names, we should swap field contents or field names instead of moving fields themselves to get the job done. That's not really friendly in javascript, so this plugin also aims to deal with that.
The demo directory is available live at http://symfony2-collection.fuz.org
Your collection type should contain prototype, allow_add, allow_delete options (depending on which buttons
you require of course). And a class that will be used as a selector to run the collection plugin.
->add('myCollection', 'collection',
array (
// ...
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'attr' => array(
'class' => 'my-selector',
),
))Then, render your form after applying the given custom theme:
{% form_theme myForm 'AcmeDemoBundle::jquery.collection.html.twig' %}
{{ form(myForm) }}Finally, put the following code at the bottom of your page.
<script src="{{ asset('js/jquery.js') }}"></script>
<script src="{{ asset('bundles/acmedemo/js/jquery.collection.js') }}"></script>
<script type="text/javascript">
$('.my-selector').collection();
</script>Notes
If you don't want to use the form theme, you should set the name_prefix option manually (see below).
If you want to use the form theme, but already use one, you can use both with:
{%
form_theme myForm
'AcmeDemoBundle::jquery.collection.html.twig'
'AcmeDemoBundle::my-own-form-theme.html.twig'
%}Customize rendered links
You can customize displayed links by setting up, down, add, deleteand duplicate options.
Default values are:
$('.collection').collection({
up: '<a href="#">▲</a>',
down: '<a href="#">▼</a>',
add: '<a href="#">[ + ]</a>',
delete: '<a href="#">[ - ]</a>',
duplicate: '<a href="#">[ # ]</a>'
});Disable links
You can disable some buttons by using allow_up, allow_down, allow_add, allow_delete
and allow_duplicate options. By default, all buttons except duplicate are enabled.
For example, if you do not want your elements to be moved up and down, use:
$('.collection').collection({
allow_up: false,
allow_down: false
});If you are using the given form theme, allow_add, allow_delete and allow_duplicate are automatically
set following your form type configuration.
Set minimum and maximum of elements in the collection
You can set the minimum of elements allowed in the collection by using the min option. By default, it is disabled (set to 0).
$('.collection').collection({
min: 0
});You can set the maximum of elements allowed in the collection by using the max option. By default, it is set to 100.
$('.collection').collection({
max: 100
});Only one add button at the bottom
If you prefer having only one add button at the bottom of the collection instead of one add button per collection element, use the add_at_the_end option:
$('.collection').collection({
add_at_the_end: true
});Events
There are before_* and after_* options that let you put callbacks before and after adding, deleting or moving
elements in the collection.
-
before_up,before_down,before_addandbefore_deleteare called before modifying the collection. The modification will be cancelled if the callback you given returnedfalse, and will proceed if it returnedtrueorundefined. -
after_up,after_down,after_addandafter_deleteare called after modifying the collection. The modification will be reverted if the callback you given returnedfalse.
Callback functions receive 2 arguments:
-
collectionreferences the div that contains your whole collection (the symfony2 field) -
elementis the element in the collection that have been added (or moved/deleted)
$('.collection').collection({
after_add: function(collection, element) {
// automatic backup or whatever
return true;
}
});Using the plugin without form theme
The form theme aims to reduce the number of options required when activating the plugin. This is really useful when you are dealing with collections of form collections. But you can still do it manually if you want, using the following equivalents:
$('.my-selector').collection({
prototype_name: '{{ myForm.myCollection.vars.prototype.vars.name }}',
enable_add: false,
enable_delete: false,
name_prefix: '{{ myForm.myCollection.vars.full_name }}'
});Note that only name_prefix option is mandatory, all other ones have default values.
Changing action's positions
By default :
add,move up,move downanddeleteare located in this order below each collection's elementaddbutton can be located at the bottom of the collection usingadd_at_the_bottomoption
You can change those button's positions by creating them manually anywhere in your form theme.
You can use any custom clickable element as soon has it has one action class:
collection-addfor anaddbuttoncollection-deletefor adeletebuttoncollection-upfor amove upbuttoncollection-downfor amove downbutton
Warning: collection is taken from the prefix option: if you change the plugin's prefix, you should change this class too.
Example:
If we have a collection of text fields and want to display actions at the right side of each value instead of below, we will use something like this:
{% block MyType_label %}{% endblock %}
{% block MyType_errors %}{% endblock %}
{% block MyType_widget %}
<div class="row">
<div class="col-md-8">
{{ form_widget(form.value) }}
</div>
<div class="col-md-2">
<a href="#" class="collection-up btn btn-default">Move up</a>
<a href="#" class="collection-down btn btn-default">Move Down</a>
</div>
<div class="col-md-2">
<a href="#" class="collection-delete btn btn-default">Remove</a>
<a href="#" class="collection-add btn btn-default">Add</a>
</div>
</div>
{% endblock %}Note : do not forget to set the add option when enabling the plugin, because if your collection is emptied,
the plugin will generate an add button based on the plugin's configuraiton.
Tip: when add buttons are put inside collection's elements, new element is created next to the clicked
element instead of at the end of the collection.
Collection of collections
This plugin has the ability to manage collection of form collections, but to avoid collisions, you should:
In your form type:
- set a distinct
prototype_nameoption and selector class for each of your collections
->add('collections', 'collection',
array (
'type' => 'collection',
'label' => 'Add, move, remove collections',
'options' => array (
'type' => 'text',
'label' => 'Add, move, remove values',
'options' => array (
'label' => 'Value',
),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'prototype_name' => '__children_name__',
'attr' => array (
'class' => "child-collection",
),
),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'prototype_name' => '__parent_name__',
'attr' => array (
'class' => "parent-collection",
),
))In the plugin options:
-
use a distinct collection prefix, so clicking
addbutton on a collection will add an item to the right collection -
define children's selector in the
selectorattribute ofchildrenoption (must select the root node of your children collections)
$('.parent-collection').collection({
prefix: 'parent',
children: [{
selector: '.child-collection',
prefix: 'child',
...
}]
});