Je construis un projet dans Symfony 2.3 en utilisant Twig. Je veux ajouter une classe au bloc de ligne de formulaire. J'utilise un fichier de thème de formulaire qui contient:
{% block form_row %}
<div class="form-row">
{{ form_label(form) }}
{{ form_widget(form) }}
{{ form_errors(form) }}
</div>
{% endblock %}
Maintenant, certaines de mes lignes de formulaire je veux ajouter une classe supplémentaire form-row-split
. Je n'arrive pas à comprendre comment faire cela correctement. Voici comment je fonctionne presque:
{% block form_row %}
{% set attr = attr|merge({'class': 'form-row' ~ (attr.class is defined ? ' ' ~ attr.class : '') ~ (errors|length > 0 ? ' error' : '')} ) %}
<div {{ block('widget_container_attributes') }}>
{{ form_label(form) }}
{{ form_widget(form) }}
{{ form_errors(form) }}
</div>
{% endblock %}
(Remarque, j'ai laissé la logique de classe error
dans celle-ci également, car elle doit rester) ... ... Puis dans le générateur de formulaire:
$builder
->add('first_name', 'text', array(
'attr' => array(
'class' => 'form-row-split'
)
));
Cela fonctionne presque mais ajoute cette classe partout et ajoute également l'identifiant du widget à la ligne!
<div id="myform_first_name" class="form-row form-row-split">
<label for="myform_first_name">First name</label>
<input id="myform_first_name" class="form-row-split" type="text" name="myform[first_name]">
</div>
Je peux penser à quelques solutions potentielles, mais aucune d’elles n’est jolie ou simple. Il doit sûrement y avoir un moyen simple de faire cela.
Il existe en fait une solution assez simple à ce problème. J'avais juste besoin d'une extension de type de formulaire pour étendre le type de formulaire de base afin d'autoriser une option supplémentaire disponible: http://symfony.com/doc/2.3/cookbook/form/create_form_type_extension.html
En suivant l'exemple de la documentation, j'ai créé une nouvelle extension de type de formulaire:
// src/Acme/FrontendBundle/Form/Extension/FormTypeExtension.php
namespace 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'];
}
}
Ensuite, j'ai enregistré le service dans mon forfait ...
<!-- 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>
Étant donné que chaque widget étend le type de formulaire de base, cela me permet de transmettre cette nouvelle option row_attr
sur n'importe quel champ, par exemple:
$builder
->add('first_name', 'text', array(
'row_attr' => array(
'class' => 'form-row-split'
)
));
Ensuite, twig se substitue pour utiliser la nouvelle option row_attr
:
{% 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 %}
Et c'est fait!
(Pour être complet, mon remplacement complet de twig fusionne toujours dans les classes form-row
et error
de la manière suivante:
{% set row_attr = row_attr|merge({'class': 'form-row' ~ (row_attr.class is defined ? ' ' ~ row_attr.class : '') ~ (errors|length > 0 ? ' error' : '')} ) %}
..mais c'est pas vraiment nécessaire pour répondre à ma propre question: P)
Docs say: vous pouvez toujours passer attr à l'élément rendu:
{{ 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) }}
Vous trouverez ci-dessous un clone de réponse par @lopsided, mais avec des modifications reflétant les dernières modifications apportées à la structure de Symfony (version 2.7+):
Il existe en fait une solution assez simple à ce problème. J'avais juste besoin d'une extension de type de formulaire pour étendre le type de formulaire de base afin d'autoriser une option supplémentaire disponible: http://symfony.com/doc/master/form/create_form_type_extension.html
En suivant l'exemple de la documentation, j'ai créé une nouvelle extension de type de formulaire:
// src/Acme/FrontendBundle/Form/Extension/FormTypeExtension.php
namespace 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'];
}
}
Ensuite, j'ai enregistré le service dans mon forfait ...
<!-- 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>
Étant donné que chaque widget étend le type de formulaire de base, cela me permet de transmettre cette nouvelle option row_attr
sur n'importe quel champ, par exemple:
$builder
->add('first_name', TextType:class, [
'row_attr' => [
'class' => 'form-row-split'
]
]);
Ensuite, twig se substitue pour utiliser la nouvelle option row_attr
:
{% 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 %}
Et c'est fait!
(Pour être complet, mon remplacement complet de twig fusionne toujours dans les classes form-row
et error
de la manière suivante:
{% set row_attr = row_attr|merge({'class': 'form-row' ~ (row_attr.class is defined ? ' ' ~ row_attr.class : '') ~ (errors|length > 0 ? ' error' : '')} ) %}
..mais c'est pas vraiment nécessaire pour répondre à ma propre question: P)
Ce que j'ai fait était plus simple (mais peut-être un peu moins propre?).
Passez la classe pour la ligne de formulaire via l'attribut "data" d'un champ:
// template.html.twig
{{ form_start(form) }}
{{ form_row(form.field, {'attr': {'data-row-class': 'my-row-class'} }) }}
{{ form_end(form) }}
Et puis manipulez-le dans le modèle de thème de formulaire de cette manière:
// form-theme.html.twig
{% block form_row -%}
{% set row_class = attr['data-row-class'] | default('') %}
<div class="{{ row_class }}">
{{- form_label(form) -}}
{{- form_widget(form) -}}
{{- form_errors(form) -}}
</div>
{%- endblock form_row %}
Ce qui donne ceci:
<form name="formName" method="post">
<div class="my-row-class">
<label for="formName_field">Field label</label>
<input type="text" id="formName_field" name="formName[field]" data-row-class="my-row-class">
</div>
</form>