web-dev-qa-db-fra.com

Symfony twig comment ajouter une classe à une ligne de formulaire

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.

15
lopsided

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)

23
lopsided

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) }}
14
pomaxa

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)

5
Pavel Dubinin

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>
1
Philippe-B-