wake-up-neo.com

Symfony-Zweig, wie eine Klasse zu einer Formularzeile hinzugefügt wird

Ich baue ein Projekt in Symfony 2.3 mit Twig. Ich möchte eine Klasse zum Formularzeilenblock hinzufügen. Ich verwende eine Formulardatei, die Folgendes enthält:

{% block form_row %}
    <div class="form-row">
        {{ form_label(form) }}
        {{ form_widget(form) }}
        {{ form_errors(form) }}
    </div>
{% endblock %}

Nun möchte ich in einigen meiner Formularzeilen eine zusätzliche Klasse form-row-split hinzufügen. Ich kann nicht herausfinden, wie ich das richtig mache. Ich habe fast gearbeitet:

{% 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 %}

(Beachten Sie, ich habe dort auch die error-Klassenlogik gelassen, da dies bleiben muss).

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

Das funktioniert fast, aber es fügt diese Klasse überall hinzu und fügt der Zeile auch die Widget-ID hinzu! 

<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>

Ich kann mir ein paar mögliche Lösungen vorstellen, aber keine davon ist hübsch oder geradlinig. Sicher muss es einen einfachen Weg geben, dies zu tun?

15
lopsided

Es gibt eine ziemlich einfache Lösung für dieses Problem. Ich brauchte nur eine Formulartyperweiterung, um den Basisformulartyp zu erweitern, um eine zusätzliche verfügbare Option zuzulassen: http://symfony.com/doc/2.3/cookbook/form/create_form_type_extension.html

Nach dem Beispiel in den Dokumenten habe ich eine neue Formulartyperweiterung erstellt:

// 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'];
    }
}

Dann habe ich den Dienst in meinem Bundle registriert ...

<!-- 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>

Da jedes Widget den Basisformulartyp erweitert, kann ich diese neue row_attr-Option in einem beliebigen Feld weitergeben, z.

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

Dann wird der Zweig überschrieben, um die neue row_attr-Option zu verwenden:

{% 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 %}

Und es ist geschafft!

(Der Vollständigkeit halber fügt sich meine vollständige Zweigüberschreibung immer noch in die Klassen form-row und error ein:

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

.. aber das ist nicht wirklich notwendig, um meine eigene Frage zu beantworten: P)

23
lopsided

Docs sagen: Sie können attr immer an gerenderte Elemente übergeben:

{{ 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

Nachfolgend finden Sie einen Klon der Antwort von @lopsided, jedoch mit den Änderungen, die die neuesten Symfony-Strukturänderungen widerspiegeln (Version 2.7 und höher):


Es gibt eine ziemlich einfache Lösung für dieses Problem. Ich brauchte nur eine Formulartyperweiterung, um den Basisformulartyp zu erweitern, um eine zusätzliche verfügbare Option zuzulassen: http://symfony.com/doc/master/form/create_form_type_extension.html

Nach dem Beispiel in den Dokumenten habe ich eine neue Formulartyperweiterung erstellt:

// 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'];
    }
}

Dann habe ich den Dienst in meinem Bundle registriert ...

<!-- 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>

Da jedes Widget den Basisformulartyp erweitert, kann ich diese neue row_attr-Option in einem beliebigen Feld weitergeben, z.

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

Dann wird der Zweig überschrieben, um die neue row_attr-Option zu verwenden:

{% 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 %}

Und es ist geschafft!

(Der Vollständigkeit halber fügt sich meine vollständige Zweigüberschreibung immer noch in die Klassen form-row und error ein:

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

.. aber das ist nicht wirklich notwendig, um meine eigene Frage zu beantworten: P)

5
Pavel Dubinin

Was ich gemacht habe, war einfacher (obwohl vielleicht etwas weniger sauber?).

Übergeben Sie die Klasse für die Formularzeile über das Attribut "data" eines Feldes:

// template.html.twig

{{ form_start(form) }}
    {{ form_row(form.field, {'attr': {'data-row-class': 'my-row-class'} }) }}
{{ form_end(form) }}

Und dann behandeln Sie es in der Formularvorlagenvorlage folgendermaßen:

// 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 %}

Was gibt das:

<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-