Hide form labels

By default, {{ form() }} and {{ form_row() }} are kind of aliases for {{ form_label() }}, {{ form_widget() }} and {{ form_errors() }}. If you want to avoid displaying useless labels for your collection elements, you should not use {{ form_row() }}, but manually render {{ form_widget() }} and {{ form_errors() }} instead.

By default,{{ form_row() }}

Tasks
0
Task
Due date
1
Task
Due date

Should Eat before March 22, 2016 00:00

Should Sleep before March 23, 2016 00:00

By using{{ form_widget() }} without form theme

Task
Due date
Task
Due date

Should Eat before March 22, 2016 00:00

Should Sleep before March 23, 2016 00:00

By using a form theme [Recommanded]

Tasks
Task
Due date
Task
Due date

Should Eat before March 22, 2016 00:00

Should Sleep before March 23, 2016 00:00


File: Controller/TroubleshootController.php
<?php

namespace Fuz\AppBundle\Controller;

use Fuz\AppBundle\Base\BaseController;
use Fuz\AppBundle\Entity\Task;
use Fuz\AppBundle\Entity\Tasks;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\HttpFoundation\Request;

/**
 * @Route("/troubleshoot")
 */
class TroubleshootController extends BaseController
{
    /**
     * Basic usage
     *
     * @Route("/hide-form-labels", name="hideFormLabels")
     * @Template()
     */
    public function hideFormLabelsAction(Request $request)
    {
        $dataA = new Tasks();
        $dataB = new Tasks();
        $dataC = new Tasks();

        $task = new Task();
        $task->setTask('Eat');
        $task->setDueDate(new \DateTime('2016-03-22'));
        $dataA->getTasks()->add($task);
        $dataB->getTasks()->add($task);
        $dataC->getTasks()->add($task);

        $task = new Task();
        $task->setTask('Sleep');
        $task->setDueDate(new \DateTime('2016-03-23'));
        $dataA->getTasks()->add($task);
        $dataB->getTasks()->add($task);
        $dataC->getTasks()->add($task);

        $formA = $this->get('form.factory')->createNamed('havingLabels', 'Fuz\AppBundle\Form\TasksType', $dataA);
        $formB = $this->get('form.factory')->createNamed('withoutLabels', 'Fuz\AppBundle\Form\TasksType', $dataB);
        $formC = $this->get('form.factory')->createNamed('withFormTheme', 'Fuz\AppBundle\Form\TasksType', $dataB);

        if ($request->isMethod('POST')) {
            $formA->handleRequest($request);
            $formB->handleRequest($request);
            $formC->handleRequest($request);
        }

        return [
            'formA' => $formA->createView(),
            'dataA' => $dataA,
            'formB' => $formB->createView(),
            'dataB' => $dataB,
            'formC' => $formC->createView(),
            'dataC' => $dataC,
        ];
    }

    /**
     * A form having a theme and containing several fields
     *
     * @Route(
     *      "/custom-jquery-version",
     *      name = "customJqueryVersion"
     * )
     * @Template()
     */
    public function customJqueryVersionAction(Request $request)
    {
        $data = ['values' => ['a', 'b', 'c']];

        $form = $this
           ->get('form.factory')
           ->createNamedBuilder('form', Type\FormType::class, $data)
           ->add('url', Type\TextType::class)
           ->add('values', Type\CollectionType::class, [
               'entry_type'    => Type\TextType::class,
               'entry_options' => [
                   'label' => 'Value',
               ],
               'label'        => 'Add, move, remove values and press Submit.',
               'allow_add'    => true,
               'allow_delete' => true,
               'prototype'    => true,
               'attr'         => [
                   'class' => 'form-collection',
               ],
           ])
           ->add('submit', Type\SubmitType::class)
           ->getForm()
        ;

        $jquery = '';
        $form->handleRequest($request);
        if ($form->isValid()) {
            $data   = $form->getData();
            $jquery = $form->getData()['url'];
        }

        return [
            'jquery' => $jquery,
            'form'   => $form->createView(),
            'data'   => $data,
        ];
    }
}
File: Controller/TroubleshootController.php
File: Entity/Task.php
<?php

namespace Fuz\AppBundle\Entity;

class Task
{
    protected $task;
    protected $dueDate;

    public function getTask()
    {
        return $this->task;
    }

    public function setTask($task)
    {
        $this->task = $task;
    }

    public function getDueDate()
    {
        return $this->dueDate;
    }

    public function setDueDate(\DateTime $dueDate = null)
    {
        $this->dueDate = $dueDate;
    }
}
File: Entity/Task.php
File: Entity/Tasks.php
<?php

namespace Fuz\AppBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;

class Tasks
{
    protected $tasks;

    public function __construct()
    {
        $this->tasks = new ArrayCollection();
    }

    public function getTasks()
    {
        return $this->tasks;
    }
}
File: Entity/Tasks.php
File: Form/TaskType.php
<?php

namespace Fuz\AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class TaskType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // want to know your form type name? try this:
        // die($this->getBlockPrefix());

        $builder
           ->add('task', 'Symfony\Component\Form\Extension\Core\Type\TextType')
           ->add('dueDate', 'Symfony\Component\Form\Extension\Core\Type\DateType')
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'Fuz\AppBundle\Entity\Task',
        ]);
    }
}
File: Form/TaskType.php
File: Form/TasksType.php
<?php

namespace Fuz\AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class TasksType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('tasks', CollectionType::class, [
            'label'        => 'Tasks',
            'entry_type'   => TaskType::class,
            'allow_add'    => true,
            'allow_delete' => true,
            'prototype'    => true,
            'required'     => false,
            'by_reference' => true,
            'delete_empty' => true,
        ]);

        $builder->add('save', SubmitType::class, [
                'label' => 'See my tasks',
        ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'Fuz\AppBundle\Entity\Tasks',
        ]);
    }
}
File: Form/TasksType.php
File: Resources/views/Troubleshoot/hideFormLabels.html.twig
{% extends 'FuzAppBundle::layout.html.twig' %}

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

{% block title %}Hide form labels{% endblock %}

{% block body %}

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

    {% verbatim %}
        <p>
            By default, <code>{{ form() }}</code> and <code>{{ form_row() }}</code> are kind of aliases for <code>{{ form_label() }}</code>,
            <code>{{ form_widget() }}</code> and <code>{{ form_errors() }}</code>. If you want to avoid displaying useless labels for your
            collection elements, you should not use <code>{{ form_row() }}</code>, but manually render <code>{{ form_widget() }}</code> and
            <code>{{ form_errors() }}</code> instead.
        </p>
    {% endverbatim %}

    <div class="row">
        <div class="col-md-4">

            {# Demo A : default behavior #}

            {% form_theme formA 'jquery.collection.html.twig' %}

            <h3>By default, {% verbatim %}{{ form_row() }}{% endverbatim %}</h3>

            {{ form_start(formA) }}
            {{ form_row(formA.tasks, {'attr': {'class': 'collectionA'} }) }}
            {{ form_end(formA) }}

            {% for task in dataA.tasks %}
                <p>Should <strong>{{ task.task }}</strong> before <strong>{{ task.dueDate|date }}</strong></p>
            {% endfor %}

        </div>
        <div class="col-md-4">

            {# Demo B : hiding labels without using form theme #}

            <h3>By using {% verbatim %}{{ form_widget() }} {% endverbatim %} without form theme</h3>

            {{ form_start(formB) }}

            <div
                id="formB-demo"
                class="collectionB"
                {% if formB.tasks.vars.prototype is defined %}
                    data-prototype="{{ form_widget(formB.tasks.vars.prototype)|e('html_attr') }}"
                    data-prototype-name="{{ formB.tasks.vars.prototype.vars.name|e('html_attr') }}"
                {% endif %}
                data-allow-add="{{ formB.tasks.vars.allow_add }}"
                data-allow-remove="{{ formB.tasks.vars.allow_delete }}"
                data-name-prefix="{{ formB.tasks.vars.full_name }}"
             >
                {% for task in formB.tasks %}
                    {{ form_widget(task) }}
                    {{ form_errors(task) }}
                {% endfor %}
            </div>

            {{ form_end(formB) }}

            {% for task in dataB.tasks %}
                <p>Should <strong>{{ task.task }}</strong> before <strong>{{ task.dueDate|date }}</strong></p>
            {% endfor %}

        </div>
        <div class="col-md-4">

            {# Demo C : using a form theme #}

            <h3>By using a form theme [Recommanded]</h3>

            {# "task" is your field type name (look at the comments in TaskType) #}
            {% block task_label %}{% endblock %}

            {# "_self" will use the block declared above to overwrite the default theme #}
            {% form_theme formC _self 'jquery.collection.html.twig' %}

            {{ form_start(formC) }}
            {{ form_row(formC.tasks, {'attr': {'class': 'collectionA'} }) }}
            {{ form_end(formC) }}

            {% for task in dataC.tasks %}
                <p>Should <strong>{{ task.task }}</strong> before <strong>{{ task.dueDate|date }}</strong></p>
            {% endfor %}

        </div>

    </div>

    <hr/>

    {{
        tabs([
            'Controller/TroubleshootController.php',
            'Entity/Task.php',
            'Form/TaskType.php',
            'Entity/Tasks.php',
            'Form/TasksType.php',
            'Resources/views/Troubleshoot/hideFormLabels.html.twig',
        ])
    }}

{% endblock %}

{% block script %}

    <script type="text/javascript">
        $('.collectionA').collection();
        $('.collectionB').collection();
        $('.collectionC').collection();
    </script>

{% endblock %}
File: Resources/views/Troubleshoot/hideFormLabels.html.twig