JavaScript options : put only one "add" button at the bottom

Use add_at_the_end option set to false (default) to create an add for each element of the collection.

Use add_at_the_end option set to true if you want to have only one add button, located below the collection.

With the option set to false

This is the default behaviour, an add button is added for each element of the collection.

Add, move, remove values and press Submit.
Value
Value
Value

Value :a

Value :b

Value :c

With the option set to true

Only one add button is available, located at the bottom of the collection.

Add, move, remove values and press Submit.
Value
Value
Value

Value :a

Value :b

Value :c


Code used:

    <script type="text/javascript">

        $('.disabled-collection').collection();

        $('.enabled-collection').collection({
            add_at_the_end: true
        });

    </script>
File: Base/BaseController.php
<?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,
        ];
    }
}
File: Base/BaseController.php
File: Controller/OptionsController.php
<?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);
    }
}
File: Controller/OptionsController.php
File: Resources/views/Options/addButtonAtTheBottom.html.twig
{% extends 'FuzAppBundle::layout.html.twig' %}

{% block extra_js %}
    <script src="{{ asset('js/jquery.collection.js') }}"></script>
{% endblock %}

{% block title %}JavaScript options : put only one "add" button at the bottom{% endblock %}

{% block body %}

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

    <p>Use <code>add_at_the_end</code> option set to <code>false</code> (default) to create an <code>add</code> for each element of the collection.</p>

    <p>Use <code>add_at_the_end</code> option set to <code>true</code> if you want to have only one <code>add</code> button, located below the collection.</p>

    <div class="row">

        {# Option add_at_the_end disabled #}
        <div class="col-md-6">
            <h3>With the option set to <code>false</code></h3>

            <p>
                This is the default behaviour, an <code>add</code> button is added for each element of the collection.
            </p>

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

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

        </div>

        {# Option add_at_the_end enabled #}
        <div class="col-md-6">
            <h3>With the option set to <code>true</code></h3>

            <p>
                Only one <code>add</code> button is available, located at the bottom of the collection.
            </p>

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

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

        </div>
    </div>

    </div>

    <hr/>

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

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

{% endblock %}

{% block script %}

    <script type="text/javascript">

        $('.disabled-collection').collection();

        $('.enabled-collection').collection({
            add_at_the_end: true
        });

    </script>

{% endblock %}
File: Resources/views/Options/addButtonAtTheBottom.html.twig