If jquery.ui.sortable
is available within your application, your form collections will be automatically
sortable using drag & drop. Do not hesitate to test this feature on all other forms in this demo website, even on
collection of form collections.
As this feature is enabled by default, set drag_drop
option to false
to disable it (see demo 1 below).
You can play with up
and down
options to hide them (see demo 2 below).
You can add custom jquery.ui.sortable
options by setting them in drag_drop_options
option (see demo 3 below).
But as start
and update
sortable options are already used by this plugin, you should use
drag_drop_start
and drag_drop_update
callbacks instead if needed (see demo 4 below).
Drag & drop do not work if allow_move_up
or allow_move_down
are disabled.
You can't move those form fields using drag & drop.
Value :a
Value :b
Value :c
Code used:
<script type="text/javascript"> $('.disabled-collection').collection({ drag_drop: false }); </script>
Just create any invisible container with collection-up
and collection-down
in
your form theme or explicitely display:none those css classes.
Value :a
Value :b
Value :c
Code used:
<style> .nobuttons-collection .collection-up, .nobuttons-collection .collection-down { display:none; } </style> <script type="text/javascript"> $('.nobuttons-collection').collection({ up: '<div style="display:none;"></div>', down: '<div style="display:none;"></div>' }); </script>
You can give any standard options to jquery.ui.sortable
by passing it to
the drag_drop_options
option.
Value :a
Value :b
Value :c
Code used:
<script type="text/javascript"> $('.moreoptions-collection').collection({ drag_drop_options: { revert: true } }); </script>
As this plugin uses the start
and update
sortable callbacks,
you should use drag_drop_start
and drag_drop_update
options
instead.
Value :a
Value :b
Value :c
Code used:
<script type="text/javascript"> var isDragging = false; $('.startupdate-collection').collection({ drag_drop_options: { stop: function (ui, event, elements, element) { if (isDragging) { $('#demo').html('<div class="alert alert-warning">Position unchanged!</div>'); isDragging = false; } } }, drag_drop_start: function (ui, event, elements, element) { $('#demo').html('<div class="alert alert-danger">Dragging....</div>'); isDragging = true; }, drag_drop_update: function (ui, event, elements, element) { $('#demo').html('<div class="alert alert-success">Position updated!</div>'); isDragging = false; } }); </script>
<?php
namespace Fuz\AppBundle\Base;
use Fuz\AppBundle\Entity\Value;
use Fuz\AppBundle\Form\ValueType;
use Fuz\QuickStartBundle\Base\BaseController as QuickStartBase;
use Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\HttpFoundation\Request;
class BaseController extends QuickStartBase
{
protected function createContextSample(Request $request, $name = 'form', $values = ['a', 'b', 'c'])
{
$data = ['values' => $values];
$form = $this
->get('form.factory')
->createNamedBuilder($name, Type\FormType::class, $data)
->add('values', Type\CollectionType::class, [
'entry_type' => Type\TextType::class,
'label' => 'Add, move, remove values and press Submit.',
'entry_options' => [
'label' => 'Value',
],
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'attr' => [
'class' => "{$name}-collection",
],
])
->add('submit', Type\SubmitType::class)
->getForm()
;
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
}
return [
$name => $form->createView(),
"{$name}Data" => $data,
];
}
protected function createAdvancedContextSample(Request $request, $name = 'advancedForm')
{
$a = new Value('a');
$b = new Value('b');
$c = new Value('c');
$data = ['values' => [$a, $b, $c]];
$form = $this
->get('form.factory')
->createNamedBuilder($name, Type\FormType::class, $data)
->add('values', Type\CollectionType::class, [
'entry_type' => ValueType::class,
'label' => 'Add, move, remove values and press Submit.',
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'attr' => [
'class' => "{$name}-collection",
],
])
->add('submit', Type\SubmitType::class)
->getForm()
;
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
}
return [
$name => $form->createView(),
"{$name}Data" => $data,
];
}
}
<?php
namespace Fuz\AppBundle\Controller;
use Fuz\AppBundle\Base\BaseController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
/**
* @Route("/options")
*/
class OptionsController extends BaseController
{
/**
* JavaScript options
*
* An overview of almost all options that you
* can enable/disable/customize on the fly.
*
* @Route("/overview", name="overview")
* @Template()
*/
public function overviewAction(Request $request)
{
return $this->createContextSample($request);
}
/**
* JavaScript options
*
* Customized buttons
*
* @Route("/customButtons", name="customButtons")
* @Template()
*/
public function customButtonsAction(Request $request)
{
return $this->createContextSample($request);
}
/**
* JavaScript options
*
* Disable buttons you don't want
*
* @Route("/enableButtons", name="enableButtons")
* @Template()
*/
public function enableButtonsAction(Request $request)
{
return array_merge(
$this->createContextSample($request), $this->createAdvancedContextSample($request)
);
}
/**
* JavaScript options
*
* Control the minimum and maximum of allowed number of elements
*
* @Route("/numberCollectionElements", name="numberCollectionElements")
* @Template()
*/
public function numberCollectionElementsAction(Request $request)
{
return array_merge(
$this->createContextSample($request), $this->createAdvancedContextSample($request)
);
}
/**
* JavaScript options
*
* Add the button close to each collection elements or only at the bottom
*
* @Route("/addButtonAtTheBottom", name="addButtonAtTheBottom")
* @Template()
*/
public function addButtonAtTheBottomAction(Request $request)
{
return array_merge(
$this->createContextSample($request, 'enabled'), $this->createContextSample($request, 'disabled')
);
}
/**
* JavaScript options
*
* Run a callback before or after adding, deleting and moving elements
*
* @Route("/eventCallbacks", name="eventCallbacks")
* @Template()
*/
public function eventCallbacksAction(Request $request)
{
return array_merge(
$this->createContextSample($request, 'eventsBefore'), $this->createContextSample($request, 'eventsAfter')
);
}
/**
* JavaScript options
*
* Use this plugin without the attached form-theme
*
* @Route("/withoutFormTheme", name="withoutFormTheme")
* @Template()
*/
public function withoutFormThemeAction(Request $request)
{
return $this->createContextSample($request);
}
/**
* JavaScript options
*
* Initialize a collection with a given minimum number of elements
*
* @Route("/givenMinimumElements", name="givenMinimumElements")
* @Template()
*/
public function givenMinimumElementsAction(Request $request)
{
return $this->createContextSample($request, 'form', []);
}
/**
* JavaScript options
*
* Hide move-up on the first item and move-down on the last one
*
* @Route("/hideMoveUpDown", name="hideMoveUpDown")
* @Template()
*/
public function hideMoveUpDownAction(Request $request)
{
return $this->createContextSample($request, 'form');
}
/**
* JavaScript options
*
* Drag & Drop allow to get rid of "move up" and "move down" buttons
*
* @Route("/dragAndDrop", name="dragAndDrop")
* @Template()
*/
public function dragAndDropAction(Request $request)
{
return [
'disabled' => $this->createAdvancedContextSample($request, 'disabled'),
'nobuttons' => $this->createAdvancedContextSample($request, 'nobuttons'),
'moreoptions' => $this->createAdvancedContextSample($request, 'moreoptions'),
'startupdate' => $this->createAdvancedContextSample($request, 'startupdate'),
];
}
/**
* JavaScript options
*
* Run a callback before and after collection initialization
*
* @Route("/initCallbacks", name="initCallbacks")
* @Template()
*/
public function initCallbacksAction(Request $request)
{
return $this->createContextSample($request);
}
/**
* JavaScript options
*
* Put buttons to custom locations in your page.
*
* @Route(
* "/buttons-custom-location",
* name = "buttonsCustomLocation"
* )
* @Template()
*/
public function buttonsCustomLocationAction(Request $request)
{
return array_merge(
$this->createContextSample($request, 'collectionA'), $this->createContextSample($request, 'collectionB'), $this->createContextSample($request, 'collectionC')
);
}
/**
* JavaScript options
*
* Enable / disable fade animation when adding / removing
* collection elements.
*
* @Route("/fadeInFadeOut", name="fadeInFadeOut")
* @Template()
*/
public function fadeInFadeOutAction(Request $request)
{
return $this->createContextSample($request);
}
}
{% extends 'FuzAppBundle::layout.html.twig' %}
{% block extra_js %}
<script src="{{ asset('js/jquery.collection.js') }}"></script>
{% endblock %}
{% block title %}JavaScript options : drag & drop{% endblock %}
{% block body %}
<h2>{{ block('title') }}</h2>
<p>
If <code>jquery.ui.sortable</code> is available within your application, your form collections will be automatically
sortable using drag & drop. Do not hesitate to test this feature on all other forms in this demo website, even on
collection of form collections.
</p>
<p>As this feature is enabled by default, set <code>drag_drop</code> option to <code>false</code> to disable it (see demo 1 below).</p>
<p>You can play with <code>up</code> and <code>down</code> options to hide them (see demo 2 below).</p>
<p>
You can add custom <code>jquery.ui.sortable</code> options by setting them in <code>drag_drop_options</code> option (see demo 3 below).
But as <code>start</code> and <code>update</code> sortable options are already used by this plugin, you should use
<code>drag_drop_start</code> and <code>drag_drop_update</code> callbacks instead if needed (see demo 4 below).
</p>
<p>Drag & drop do not work if <code>allow_move_up</code> or <code>allow_move_down</code> are disabled.</p>
<div class="row">
{# demo 1: drag & drop disabled #}
<div class="col-md-6 col-sm-12 col-xs-12">
<h4>Demo 1: drag & drop disabled</h4>
<p>
You can't move those form fields using drag & drop.
</p>
{%
form_theme disabled.disabled
'jquery.collection.html.twig'
'FuzAppBundle:Options:options-theme.html.twig'
%}
{{ form(disabled.disabled) }}
<hr/>
{% for value in disabled.disabledData.values %}
<p>Value : {{ value }}</p>
{% endfor %}
<hr/>
<p>Code used:</p>
<pre>{{ block('script_disabled') | e }}</pre>
</div>
{# demo 2: hidden move up/down buttons #}
<div class="col-md-6 col-sm-12 col-xs-12">
<h4>Demo 2: hidden move up/down buttons</h4>
<p>
Just create any invisible container with <code>collection-up</code> and <code>collection-down</code> in
your form theme or explicitely display:none those css classes.
</p>
{%
form_theme nobuttons.nobuttons
'jquery.collection.html.twig'
'FuzAppBundle:Options:options-theme.html.twig'
%}
{{ form(nobuttons.nobuttons) }}
<hr/>
{% for value in nobuttons.nobuttonsData.values %}
<p>Value : {{ value }}</p>
{% endfor %}
<hr/>
<p>Code used:</p>
<pre>{{ block('script_nobuttons') | e }}</pre>
</div>
{# demo 3: customize sortable options #}
<div class="col-md-6 col-sm-12 col-xs-12">
<h4>Demo 3: customize sortable options</h4>
<p>
You can give any standard options to <code>jquery.ui.sortable</code> by passing it to
the <code>drag_drop_options</code> option.
</p>
{%
form_theme moreoptions.moreoptions
'jquery.collection.html.twig'
'FuzAppBundle:Options:options-theme.html.twig'
%}
{{ form(moreoptions.moreoptions) }}
<hr/>
{% for value in moreoptions.moreoptionsData.values %}
<p>Value : {{ value }}</p>
{% endfor %}
<hr/>
<p>Code used:</p>
<pre>{{ block('script_moreoptions') | e }}</pre>
</div>
{# demo 4: use start/update sortable options #}
<div class="col-md-6 col-sm-12 col-xs-12">
<h4>Demo 4: use start/update sortable options</h4>
<p>
As this plugin uses the <code>start</code> and <code>update</code> sortable callbacks,
you should use <code>drag_drop_start</code> and <code>drag_drop_update</code> options
instead.
</p>
<div id="demo"><div class="alert alert-warning">Please drag & drop...</div></div>
{%
form_theme startupdate.startupdate
'jquery.collection.html.twig'
'FuzAppBundle:Options:options-theme.html.twig'
%}
{{ form(startupdate.startupdate) }}
<hr/>
{% for value in startupdate.startupdateData.values %}
<p>Value : {{ value }}</p>
{% endfor %}
<hr/>
<p>Code used:</p>
<pre>{{ block('script_startupdate') | e }}</pre>
</div>
</div>
{{
tabs([
'Base/BaseController.php',
'Controller/OptionsController.php',
'Resources/views/Options/dragAndDrop.html.twig',
])
}}
{% endblock %}
{% block script %}
{% block script_disabled %}
<script type="text/javascript">
$('.disabled-collection').collection({
drag_drop: false
});
</script>
{% endblock %}
{% block script_nobuttons %}
<style>
.nobuttons-collection .collection-up, .nobuttons-collection .collection-down {
display:none;
}
</style>
<script type="text/javascript">
$('.nobuttons-collection').collection({
up: '<div style="display:none;"></div>',
down: '<div style="display:none;"></div>'
});
</script>
{% endblock %}
{% block script_moreoptions %}
<script type="text/javascript">
$('.moreoptions-collection').collection({
drag_drop_options: {
revert: true
}
});
</script>
{% endblock %}
{% block script_startupdate %}
<script type="text/javascript">
var isDragging = false;
$('.startupdate-collection').collection({
drag_drop_options: {
stop: function (ui, event, elements, element) {
if (isDragging) {
$('#demo').html('<div class="alert alert-warning">Position unchanged!</div>');
isDragging = false;
}
}
},
drag_drop_start: function (ui, event, elements, element) {
$('#demo').html('<div class="alert alert-danger">Dragging....</div>');
isDragging = true;
},
drag_drop_update: function (ui, event, elements, element) {
$('#demo').html('<div class="alert alert-success">Position updated!</div>');
isDragging = false;
}
});
</script>
{% endblock %}
{% endblock %}