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 )