Symfony学习笔记之表单渲染-----form总结

        Symfony为您提供了几种自定义表单渲染的方法。在本文中,您将学习如何对表单的一个或多个字段进行单一自定义。如果您需要以相同的方式自定义所有表单,请创建表单主题 或 使用任何内置主题,例如Symfony表单 的 Bootstrap主题 

1、表单渲染函数

        单个调用Twig函数 form()就足以呈现整个表单,包括其所有字段和错误消息:

{# form is a variable passed from the controller and created
  by calling to the $form->createView() method #}
{{ form(form) }}

        下一步是使用form_start(), form_end(), form_errors()和 form_row() Twig函数来呈现不同的表单部分,以便您可以自定义添加HTML元素和属性:

        该form_row()函数输出整个字段内容,包括标签,帮助消息,HTML元素和错误消息。所有这些都可以使用其他Twig函数进一步自定义,如下图所示:

如下图所示

        该form_label() , form_widget() , form_help()和 form_errors()小枝功能,可以让您对每个表单域的渲染进行控制,这样你就可以完全自定义它们:

<div class="form-control">
    <i class="fa fa-calendar"></i> {{ form_label(form.dueDate) }}
    {{ form_widget(form.dueDate) }}

    <small>{{ form_help(form.dueDate) }}</small>

    <div class="form-error">
        {{ form_errors(form.dueDate) }}
    </div>
</div>

        在本文的后面部分,您可以找到这些 Twig函数 的完整参考,以及更多用法示例。

2、表单渲染变量

        上一节中提到的一些Twig函数,允许传递变量来配置它们的行为。例如,该form_label() 函数允许您定义自定义标签,以覆盖表单中定义的标签:

{{ form_label(form.task, 'My Custom Task Label') }}

        某些 表单字段类型 具有可以传递给 widget 的其它渲染选项。每种类型都记录了这些选项,但有一个常见选项attr,它允许您修改表单元素上的HTML属性。以下将名字为 task_field 的CSS类添加到已经渲染的文本类型的输入字段task:

{{ form_widget(form.task, {'attr': {'class': 'task_field'}}) }}

        如果您一次渲染整个表单(或整个嵌入表单),则该variables参数(即form_widget的第2个参数)将仅应用于表单本身,而不是其子项。换句话说,以下内容不会 “foo” class属性传递给表单中的所有子字段:

{# does **not** work - the variables are not recursive #}
{{ form_widget(form, { 'attr': {'class': 'foo'} }) }}

        如果需要 “手动”渲染表单字段,则可以使用其属性访问字段(例如idnamelabel)的 各个vars值。例如,得到id

{{ form.task.vars.id }}

        在本文后面,您可以找到这些Twig变量及其描述的完整参考。

3、如何使用表单主题

        前面部分中显示的Twig函数和变量,详细可见symfony学习笔记之模板渲染-----twig总结,可以帮助您自定义表单的一个或多个字段。但是,此自定义无法应用于表单的其余部分。

        本文将介绍,如何在您的应用程序中使用Symfony提供的任何表单主题,以及如何创建自己的自定义表单主题。

1)Symfony内置表格主题

        Symfony带有几个内置的表单主题,使用一些最流行的CSS框架时,您的表单看起来很棒。每个主题都在一个Twig模板中定义:

提示:

        阅读有关Bootstrap 4 Symfony表单主题的文章, 以了解有关它的更多信息。

2)将主题应用于所有表单

        Symfony默认使用 form_div_layout.html.twig主题。如果要对应用的所有形式使用其他主题,请在 twig.form_themes选项中进行配置 :

# config/packages/twig.yaml
twig:
    form_themes: ['bootstrap_4_horizontal_layout.html.twig']
    # ...

<!-- config/packages/twig.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:twig="http://symfony.com/schema/dic/twig"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd">

    <twig:config>
        <twig:form-theme>bootstrap_4_horizontal_layout.html.twig</twig:form-theme>
        <!-- ... -->
    </twig:config>
</container>

// config/packages/twig.php
$container->loadFromExtension('twig', [
    'form_themes' => [
        'bootstrap_4_horizontal_layout.html.twig',
    ],
    // ...
]);

        您可以将多个主题传递给此选项,因为有时表单主题仅重新定义了一些元素。这样,如果某个主题没有覆盖某个元素,Symfony会查找其他主题。twig.form_themes选项中主题的顺序很重要。每个主题都会覆盖所有以前的主题,因此您必须将最重要的主题放在列表的末尾。

3)将主题应用于单个表单

        虽然大多数时候,您都会在全局范围内应用表单主题,但您可能只需要将一个主题应用于某个特定表单。你可以使用Twig标签 form_theme 来做到这 一点

{# this form theme will be applied only to the form of this template #}
{% form_theme form 'foundation_5_layout.html.twig' %}

{{ form_start(form) }}
    {# ... #}
{{ form_end(form) }}

    form_theme标记的第一个参数(在此示例中的form参数)是存储 表单视图对象 的变量的名称。第二个参数是定义表单主题的Twig模板的路径。

4)将多个主题应用于单个表单

        还可以通过应用多个主题,来自定义表单。为此,使用 with关键字 将所有Twig模板的路径作为数组传递(它们的顺序很重要,因为每个主题都会覆盖所有以前的主题):

{# apply multiple form themes but only to the form of this template #}
{% form_theme form with [
    'foundation_5_layout.html.twig',
    'forms/my_custom_theme.html.twig'
] %}

{# ... #}

5)将不同主题应用于子表单

        您还可以将表单主题,应用于表单的特定子项:

{% form_theme form.a_child_form 'form/my_custom_theme.html.twig' %}

        当您希望为嵌套表单,创建一个与主表单不同的自定义主题时,这非常有用。指定两个主题:

{% form_theme form 'form/my_custom_theme.html.twig' %}
{% form_theme form.a_child_form 'form/my_other_theme.html.twig' %}

6)禁用单个表单的全局主题

        应用程序中定义的全局表单主题,始终应用于所有表单,甚至是那些使用 form_theme标记应用自己主题的表单。您可能希望禁用此功能,例如在为某个bundle创建管理界面时,且这个bundle也可以安装到其它应用程序上(因此您无法控制全局启用的主题)。为此,请在表单主题列表后,添加关键字 only

{# 可能被应用于不同应用程序中 #}
{% form_theme form with ['foundation_5_layout.html.twig'] only %}

{# ... #}

        使用 only关键字 时,不会应用Symfony的内置表单主题(form_div_layout.html.twig等)。为了正确渲染表单,您需要自己提供功能齐全的表单主题,或者使用Twig的 use关键字 扩展其中一个内置表单主题,而不是 extends重复使用原始主题内容。

{# templates/form/common.html.twig #}
{% use "form_div_layout.html.twig" %}

{# ... #}

4、创建自己的表单主题

        Symfony使用 Twig块 来渲染表单的每个部分 - 字段标签,错误errors, <input>文本字段,<select>标签等。主题 是一个Twig模板,其中包含着您在渲染表单时要使用的一个或多个的这样的块。

        例如,考虑渲染一个整数属性的表单字段 age。如果将其添加到模板:

{{ form_widget(form.age) }}

        生成的HTML内容将是这样的,(它将根据您的应用程序中启用的表单主题而有所不同):

<input type="number" id="form_age" name="form[age]" required="required" value="33" />

        Symfony使用一个称为 integer_widget” Twig块来渲染该字段。这是因为字段类型是integer,并且您正在渲染它的widget小部件(而不是它labelerrorshelp)。创建表单主题的第一步是,知道要覆盖哪个Twig块,如以下部分所述。

    1)表单片段命名

        表单片段的命名,取决于您的需求:

  • 如果要自定义相同类型的所有字段(例如全部<textarea>),请使用field-type_field-part模式(例如textarea_widget)。
  • 如果您只想自定义一个特定字段(例如,<textarea> 用于description编辑产品的表单字段),请使用该_field-id_field-part模式(例如_product_description_widget)。

        在这两种情况下,field-part可以是以下任何有效的表单字段部分(以下各部分都称为Twig块或片段,):

以下任何有效的表单字段部分

     2)相同类型的所有字段的片段命名

        这些片段名称遵循type_part的模式,其中type 对应于被渲染的字段类型(例如textareacheckbox, date,等等),并且part对应于正在渲染的(例如 labelwidget等)

        片段名称的一些示例是:

     3)单个字段的片段命名

        这些片段名称遵循_field-id_part模式,其中 field-id对应于字段的id属性(含表单名称字段名称,例如product_description, user_age等),并且part对应于正在渲染的(例如labelwidget等)

        该field-id,包含表单名称字段名称(例如 product_price)。表单名称可以手动设置,也可以根据表单类型名称自动生成(例如ProductType等同于product)。如果您不确定表单名称是什么,请查看为表单渲染的HTML代码。您还可以使用以下 [block_name选项] 显式定义此 [字段名称值] : 

use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;

public function buildForm(FormBuilderInterface $builder, array $options)
{
    // ...

    $builder->add('name', TextType::class, [
        'block_name' => 'custom_name',
    ]); # 'block_name'用于定义 基于表单字段的Twig块的名字
}

        在此示例中,片段名称将_product_custom_name_widget 取代默认值_product_name_widget(默认field-id为product_name)

    4)表单集合的片段命名

        使用表单集合时,每个集合项(即表单)的片段都要遵循预定义的模式。例如,考虑以下复杂示例,其中 TaskManagerType的集合 TaskListType,又包含以下集合TaskType

class TaskManagerType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options = array())
    {
        // ...
        $builder->add('taskLists', CollectionType::class, array(
            'entry_type' => TaskListType::class, # 一个entry如taskLists,可能包含好几个entry_type即entry
            'block_name' => 'task_lists',
        ));
    }
}

class TaskListType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options = array())
    {
        // ...
        $builder->add('tasks', CollectionType::class, array(
            'entry_type' => TaskType::class, # 一个entry如tasks,可能包含好几个entry_type即entry
        ));
    }
}

class TaskType
{
    public function buildForm(FormBuilderInterface $builder, array $options = array())
    {
        $builder->add('name');
        // ...
    }
}

        然后你得到以下所有定制的块(其中,*可以被替换 rowwidgetlabel,或help):

{% block _task_manager_task_lists_* %}
    {# the collection field of TaskManager #}
{% endblock %}

{% block _task_manager_task_lists_entry_* %}
    {# the inner TaskListType #}
{% endblock %}

{% block _task_manager_task_lists_entry_tasks_* %}
    {# the collection field of TaskListType #}
{% endblock %}

{% block _task_manager_task_lists_entry_tasks_entry_* %}
    {# the inner TaskType #}
{% endblock %}

{% block _task_manager_task_lists_entry_tasks_entry_name_* %}
    {# the field of TaskType #}
{% endblock %}

    5)模板片段继承

        每个字段类型都有类型(例如,textarea父类型为 texttext父类型为 form),如果基本片段不存在,Symfony将使用片段 fragement 作为父类型。

        当Symfony渲染一个textarea类型的错误时,它首先要渲染textarea_errors片段,然后依次回溯地渲染text_errors 和form_errors片段。

        Symfony支持的字段类型有:

        a)文本字段 Text Fields:

        b)选择字段 Choice Fields:

        c)日期和时间字段 Date and Time Fields:

        d)其他字段 Other Fields:

        e)字段组 Field Groups:

        f)隐藏字段 Hidden Fields:

        g)按钮 Buttons:

        h)基本字段 Base Fields:

    6)单个表单模板的特殊化主题

        在对应用中的 单个表单 进行特定的自定义时,建议使用此方法。例如更改表单的所有<textarea>元素,或自定义一个将使用JavaScript处理的非常特殊的表单字段。

        您只需要将特殊的{% form_theme form _self %}标记添加到渲染表单的模板中,并且将这个模板继承自你的基本模板。这使得任何被覆盖的表单块仅在当前模板中被重新定义:

{% extends 'base.html.twig' %}

{% form_theme form _self %}

{# this overrides the widget of any field of type integer, but only in the
   forms rendered inside this template #}
{% block integer_widget %}
    <div class="...">
        {# ... render the HTML element to display this field ... #}
    </div>
{% endblock %}

{# this overrides the entire row of the field whose "id" = "product_stock" (and whose
   "name" = "product[stock]") but only in the forms rendered inside this template #}
{% block _product_stock_row %}
    <div class="..." id="...">
        {# ... render the entire field contents, including its errors ... #}
    </div>
{% endblock %}

{# ... render the form ... #}

        这种方法的主要缺点是,它只有在你的模板扩展另一个时才有效(在前面的例子中扩展自'base.html.twig')。如果您的模板没有扩展时,则必须指向form_theme单独的模板,如下一节中所述。

        另一个缺点是,在其它他模板中渲染其它表单时,无法重用自定义表单块。如果这就是您所需要的,请在单独的模板中创建表单主题,如下一节中所述。

    7)在单独的模板中创建表单主题

        在创建整个应用程序中使用的表单主题,或甚至在不同的Symfony应用程序中重用时,建议使用此方法。您只需要在某处创建一个Twig模板,并按照 表单片段命名 规则来定义Twig块。

        例如,如果表单主题很简单,并且您只想覆盖 <input type="integer">元素,请创建此模板:

{# templates/form/my_theme.html.twig #}
{% block integer_widget %}

    {# ... add all the HTML, CSS and JavaScript needed to render this field #}

{% endblock %}

        现在您需要告诉Symfony,使用此表单主题而不是(或除此之外)默认主题。如本文前面部分所述,如果要将全局主题应用于所有表单,请定义 twig.form_themes选项:

# config/packages/twig.yaml
twig:
    form_themes: ['form/my_theme.html.twig']
    # ...

<!-- config/packages/twig.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:twig="http://symfony.com/schema/dic/twig"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd">

    <twig:config>
        <twig:form-theme>form/my_theme.html.twig</twig:form-theme>
        <!-- ... -->
    </twig:config>
</container>

// config/packages/twig.php
$container->loadFromExtension('twig', [
    'form_themes' => [
        'form/my_theme.html.twig',
    ],
    // ...
]);

        如果您只想将其应用于某些特定表单,请使用以下form_theme标记:

{% form_theme form 'form/my_theme.html.twig' %}

{{ form_start(form) }}
    {# ... #}
{{ form_end(form) }}

    8)部分重用表单的内置主题

        创建完整的表单主题需要大量的工作,因为有太多不同的表单字段类型。您可以只定义您感兴趣的块,然后在您的应用或模板中配置多个表单主题,而不是定义所有这些Twig块。这样做是因为,在渲染未在自定义主题中覆盖的块时,Symfony会回退到其他主题。

        另一种解决方案是,使用Twig“use”标签而不是extends标签,使表单主题模板从其中一个内置主题扩展,这样您就可以继承其所有块(如果您不确定,则从默认 form_div_layout.html.twig主题扩展 ):

{# templates/form/my_theme.html.twig #}
{% use 'form_div_layout.html.twig' %}

{# ... override only the blocks you are interested in #}

        最后,您还可以使用 Twig parent()函数 重用内置主题的原始内容。当您只想进行微小更改时,这很有用,例如使用某个元素包装生成的HTML:

{# templates/form/my_theme.html.twig #}
{% use 'form_div_layout.html.twig' %}

{% block integer_widget %}
    <div class="some-custom-class">
        {{ parent() }}
    </div>
{% endblock %}

        在渲染表单的模板中定义表单主题时,此技术也有效。但是,从内置主题导入块有点复杂:

{% form_theme form _self %}

{# import a block from the built-in theme and rename it so it doesn't
   conflict with the same block defined in this template #}
{% use 'form_div_layout.html.twig' with integer_widget as base_integer_widget %}

{% block integer_widget %}
    <div class="some-custom-class">
        {{ block('base_integer_widget') }}
    </div>
{% endblock %}


{# ... render the form ... #}

    9)自定义表单验证错误

        如果为对象定义验证规则,则在提交的数据无效时,您将看到一些验证错误消息。这些消息与form_errors()函数一起显示,并且可以使用 form_errors任何表单主题中的Twig块进行自定义,如前面部分所述。

        需要考虑的一个重要事项是,某些错误与整个表单而不是特定字段相关联。为了区分全局错误和本地错误,请使用 表单中的一个名为 compound 的变量。如果是true,则意味着当前渲染的是字段集合(例如整个表单),而不仅仅是单个字段:

{# templates/form/my_theme.html.twig #}
{% block form_errors %}
    {% if errors|length > 0 %}
        {% if compound %}
            {# ... display the global form errors #}
            <ul>
                {% for error in errors %}
                    <li>{{ error.message }}</li>
                {% endfor %}
            </ul>
        {% else %}
            {# ... display the errors for a single field #}
        {% endif %}
    {% endif %}
{% endblock form_errors %}

        本作品(包括代码示例),遵循 Creative Commons BY-SA 3.0 许可。


参考 1:如何自定义表单渲染 https://symfony.com/doc/current/form/form_customization.html

参考 2:如何去控制表单渲染 http://www.symfonychina.com/doc/current/form/rendering.html

参考 3:如何自定义表单渲染 http://www.symfonychina.com/doc/current/form/form_customization.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值