Symfony twig how to add class to a form row Symfony twig how to add class to a form row symfony symfony

Symfony twig how to add class to a form row


There is a fairly simple solution to this problem actually. I just needed a form type extension to extend the base form type to allow an extra available option: http://symfony.com/doc/2.3/cookbook/form/create_form_type_extension.html

Following through the example in the docs, I created a new form type extension:

// src/Acme/FrontendBundle/Form/Extension/FormTypeExtension.phpnamespace Acme\FrontendBundle\Form\Extension;use Symfony\Component\Form\AbstractTypeExtension;use Symfony\Component\Form\FormInterface;use Symfony\Component\Form\FormView;use Symfony\Component\OptionsResolver\OptionsResolverInterface;/** * Class FormTypeExtension * @package Acme\FrontendBundle\Form\Extension */class FormTypeExtension extends AbstractTypeExtension{    /**     * Extends the form type which all other types extend     *     * @return string The name of the type being extended     */    public function getExtendedType()    {        return 'form';    }    /**     * Add the extra row_attr option     *     * @param OptionsResolverInterface $resolver     */    public function setDefaultOptions(OptionsResolverInterface $resolver)    {        $resolver->setDefaults(array(            'row_attr' => array()        ));    }    /**     * Pass the set row_attr options to the view     *     * @param FormView $view     * @param FormInterface $form     * @param array $options     */    public function buildView(FormView $view, FormInterface $form, array $options)    {        $view->vars['row_attr'] = $options['row_attr'];    }}

Then I registered the service in my bundle...

<!-- Form row attributes form extension --><service id="acme.form_type_extension" class="Acme\FrontendBundle\Form\Extension\FormTypeExtension">    <tag name="form.type_extension" alias="form" /></service>

Since every widget extends the base form type this then allows me to pass this new row_attr option through on any field, eg:

$builder    ->add('first_name', 'text', array(        'row_attr' => array(            'class' => 'form-row-split'        )    ));

Then the twig overrides to make use of the new row_attr option:

{% block form_row %}    <div {{ block('form_row_attributes') }}>        {{ form_label(form) }}        {{ form_widget(form) }}        {{ form_errors(form) }}    </div>{% endblock form_row %}{% block form_row_attributes %}    {% spaceless %}        {% for attrname, attrvalue in row_attr %}{{ attrname }}="{{ attrvalue }}" {% endfor %}    {% endspaceless %}{% endblock form_row_attributes %}

And it's done!

(For completeness, my full twig override still merges in the form-row and error classes in like so:

{% set row_attr = row_attr|merge({'class': 'form-row' ~ (row_attr.class is defined ? ' ' ~ row_attr.class : '') ~ (errors|length > 0 ? ' error' : '')} ) %}

.. but thats not really necessary for answering my own question :P )


Docs say: you always able to pass attr to rendered element:

{{ form_start(form, {'attr': {'class': 'your-class'}} ) }}    {{ form_label(form, {'attr': {'class': 'your-class'}}) }}    {{ form_widget(form, {'attr': {'class': 'your-class'}}) }}    {{ form_errors(form, {'attr': {'class': 'your-class'}}) }}{{ form_end(form) }}


Below is a clone of answer by @lopsided but with changes reflecting latest Symfony structure changes (v. 2.7+):


There is a fairly simple solution to this problem actually. I just needed a form type extension to extend the base form type to allow an extra available option: http://symfony.com/doc/master/form/create_form_type_extension.html

Following through the example in the docs, I created a new form type extension:

// src/Acme/FrontendBundle/Form/Extension/FormTypeExtension.phpnamespace Acme\FrontendBundle\Form\Extension;use Symfony\Component\Form\AbstractTypeExtension;use Symfony\Component\Form\FormInterface;use Symfony\Component\Form\FormView;use Symfony\Component\Form\Extension\Core\Type\FormType;use Symfony\Component\OptionsResolver\OptionsResolver;/** * Class FormTypeExtension * @package Acme\FrontendBundle\Form\Extension */class FormTypeExtension extends AbstractTypeExtension{    /**     * Extends the form type which all other types extend     *     * @return string The name of the type being extended     */    public function getExtendedType()    {        return FormType::class;    }    /**     * Add the extra row_attr option     *     * @param OptionsResolver $resolver     */    public function configureOptions(OptionsResolver $resolver)    {        $resolver->setDefaults(array(            'row_attr' => []        ));    }    /**     * Pass the set row_attr options to the view     *     * @param FormView $view     * @param FormInterface $form     * @param array $options     */    public function buildView(FormView $view, FormInterface $form, array $options)    {        $view->vars['row_attr'] = $options['row_attr'];    }}

Then I registered the service in my bundle...

<!-- Form row attributes form extension --><service id="acme.form_type_extension" class="Acme\FrontendBundle\Form\Extension\FormTypeExtension">    <tag name="form.type_extension" alias="form" extended_type="Symfony\Component\Form\Extension\Core\Type\FormType" /></service>

Since every widget extends the base form type this then allows me to pass this new row_attr option through on any field, eg:

$builder    ->add('first_name', TextType:class, [        'row_attr' => [            'class' => 'form-row-split'        ]    ]);

Then the twig overrides to make use of the new row_attr option:

{% block form_row %}    <div {{ block('form_row_attributes') }}>        {{ form_label(form) }}        {{ form_widget(form) }}        {{ form_errors(form) }}    </div>{% endblock form_row %}{% block form_row_attributes %}    {% spaceless %}        {% for attrname, attrvalue in row_attr %}{{ attrname }}="{{ attrvalue }}" {% endfor %}    {% endspaceless %}{% endblock form_row_attributes %}

And it's done!

(For completeness, my full twig override still merges in the form-row and error classes in like so:

{% set row_attr = row_attr|merge({'class': 'form-row' ~ (row_attr.class is defined ? ' ' ~ row_attr.class : '') ~ (errors|length > 0 ? ' error' : '')} ) %}

.. but thats not really necessary for answering my own question :P )