JavaScript options : events on buttons

Use before_up, before_down, before_add, before-duplicate and before_remove options to set a callback before applying an action to the collection.

Use after_up, after_down, after_add, after-duplicate and after_remove options to set a callback after applying an action to the collection.

Callback takes 2 arguments: collection contains the collection's root node and element the element in the collection that have been added, moved or deleted.

Note: element may be undefined on before_add event when add_at_the_end is set to true or when collection has no elements yet.

Your callback should return true or undefined to apply the action else it is cancelled or rollbacked.

Events before updating the collection

Value : a

Value : b

Value : c

Events after updating the collection

Value : a

Value : b

Value : c


Code used:

    <script type="text/javascript">

        $('.eventsBefore-collection').collection({
            before_up: function(collection, element) { return confirm("Do you really want to move the element up?"); },
            before_down: function(collection, element) { return confirm("Do you really want to move the element down?"); },
            before_add: function(collection, element) { return confirm("Do you really want to add the element?"); },
            before_remove: function(collection, element) { return confirm("Do you really want to delete the element?"); },
            before_duplicate: function(collection, element) { return confirm("Do you really want to duplicate the element?"); },
            allow_duplicate: true
        });

        $('.eventsAfter-collection').collection({
            after_up: function(collection, element) { return confirm("Do you really want to move the element up?"); },
            after_down: function(collection, element) { return confirm("Do you really want to move the element down?"); },
            after_add: function(collection, element) { return confirm("Do you really want to add the element?"); },
            after_remove: function(collection, element) { return confirm("Do you really want to delete the element?"); },
            after_duplicate: function(collection, element) { return confirm("Do you really want to duplicate the element?"); },
            allow_duplicate: true
        });

    </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 : events on buttons{% endblock %}

{% block body %}

    <h2>{{ block('title') }}</h2>

    <p>
        Use <code>before_up</code>, <code>before_down</code>, <code>before_add</code>, <code>before-duplicate</code> and <code>before_remove</code>
        options to set a callback <strong>before</strong> applying an action to the collection.
    </p>

    <p>
        Use <code>after_up</code>, <code>after_down</code>, <code>after_add</code>, <code>after-duplicate</code> and <code>after_remove</code>
        options to set a callback <strong>after</strong> applying an action to the collection.
    </p>

    <p>
        Callback takes 2 arguments: <code>collection</code> contains the collection's root node and <code>element</code> the element in the
        collection that have been added, moved or deleted.
    </p>

    <p>
        Note: <code>element</code> may be <code>undefined</code> on <code>before_add</code> event when <code>add_at_the_end</code>
        is set to <code>true</code> or when collection has no elements yet.
    </p>

    <p>Your callback should return <code>true</code> or <code>undefined</code> to apply the action else it is cancelled or rollbacked.</p>

    <div class="row">

        <div class="col-md-6">
            <h3>Events before updating the collection</h3>

             {% form_theme eventsBefore 'jquery.collection.html.twig' %}
            {{ form(eventsBefore) }}

            {% for value in eventsBeforeData.values %}
                <p>Value : {{ value }}</p>
            {% endfor %}

        </div>
        <div class="col-md-6">
            <h3>Events after updating the collection</h3>

            {% form_theme eventsAfter 'jquery.collection.html.twig' %}
            {{ form(eventsAfter) }}

            {% for value in eventsAfterData.values %}
                <p>Value : {{ value }}</p>
            {% endfor %}

        </div>

    </div>

    <hr/>

    <p>Code used:</p>
    <pre>{{ block('script') | e }}</pre>

    {{
        tabs([
            'Base/BaseController.php',
            'Controller/OptionsController.php',
            'Resources/views/Options/eventCallbacks.html.twig',
        ])
    }}

{% endblock %}

{% block script %}

    <script type="text/javascript">

        $('.eventsBefore-collection').collection({
            before_up: function(collection, element) { return confirm("Do you really want to move the element up?"); },
            before_down: function(collection, element) { return confirm("Do you really want to move the element down?"); },
            before_add: function(collection, element) { return confirm("Do you really want to add the element?"); },
            before_remove: function(collection, element) { return confirm("Do you really want to delete the element?"); },
            before_duplicate: function(collection, element) { return confirm("Do you really want to duplicate the element?"); },
            allow_duplicate: true
        });

        $('.eventsAfter-collection').collection({
            after_up: function(collection, element) { return confirm("Do you really want to move the element up?"); },
            after_down: function(collection, element) { return confirm("Do you really want to move the element down?"); },
            after_add: function(collection, element) { return confirm("Do you really want to add the element?"); },
            after_remove: function(collection, element) { return confirm("Do you really want to delete the element?"); },
            after_duplicate: function(collection, element) { return confirm("Do you really want to duplicate the element?"); },
            allow_duplicate: true
        });

    </script>

{% endblock %}