Symfony Twig:为集合自定义单个字段

8
在Symfony文档中,有一种方法可以根据小部件的名称/ID来自定义单个字段。具体请参考此处
{% form_theme form _self %}

{% block _product_name_widget %}
    <div class="text_widget">
        {{ block('field_widget') }}
    </div>
{% endblock %}

{{ form_widget(form.name) }}

在这里,_product_name_widget片段定义了用于id为product_name(名称为product[name])的字段的模板。

这对于普通小部件有效,但在小部件位于集合内部时无效。由于额外的列。 就像这样:

name="productbundle_product_type[foobar][1][value]" id="productbundle_product_type_foobar_1_value"

如何使Twig自定义在集合内生效?

我尝试了以下方法,但无法生效:

{% for db in edit_form.list %}
    {% block _productbundle_product_type_foobar_{{ db.name }}_widget %}
        <div class="text_widget">
            {{ block('field_widget') }}
        </div>
    {% endblock %}
{% endfor %}

即使是下面的代码也无法工作:
{% _productbundle_product_type_foobar_1_value_widget %}

如何使其工作?


如果您使用{% block _productbundle_product_type_foobar_1_{{ db.name }}_widget %}(带有_1_), - cheesemacfly
不,以下代码也不起作用:{% _productbundle_product_type_foobar_1_value_widget %} - user2382765
我感到惊讶,因为文档中写道:“在这里,_product_name_widget片段定义了要用于id为product_name(名称为product [name])的字段的模板。”因此,如果您的小部件名称为“productbundle_product_type [foobar] [1] [value]”,则应该能够使用{% block _productbundle_product_type_foobar_1_value_widget%} - cheesemacfly
文档应该是指“正常”的表单小部件,而不是集合中的表单小部件。但确实很奇怪。这就是我提出问题的原因。 :-) - user2382765
3个回答

3
我之前在一项项目中遇到了这个问题 - 我找到的解决方案是一组看起来像这样的块(去掉了特定于项目的代码):
{# Collection markup #}
{% block my_collection_widget %}
    {# Customise collection row prototype for allow_add #}
    {% if prototype is defined %}
        {% set data_prototype = block('my_collection_item_widget')  %}
        <div id="my_prototype" data-prototype="{{ data_prototype }}" style="display: none"></div>
    {% endif %}

    {% for prototype in form %}
        {{ block('my_collection_item_widget') }}
    {% endfor %}
{% endblock my_collection_widget %}

{# Collection row markup #}
{% block my_collection_item_widget %}
    {# Collection contains simple, single type #}
    {{ form_errors(prototype) }}
    {{ form_label(prototype) }}
    {{ form_widget(prototype) }}

    {# OR #}

    {# Collection contains a compound type, render child types independantly #}
    {{ form_errors(prototype.inner_widget) }}
    {{ form_label(prototype.inner_widget) }}
    {{ form_widget(prototype.inner_widget) }}
{% endblock my_collection_item_widget %}

"inner_widget" 方法不存在。"inner_widget" 方法,我的 My_collection_widget 名称需要什么?_productbundle_product_type_foobar_widget 无法使用。我猜它不符合我所需的要求。 - user2382765
在两个块中都将'my_collection'替换为您正在呈现的表单名称。在*item_widget块内,您可以提供集合中单个项目的自定义标记; 如果您的集合很简单(只有一种类型),则只需使用{{ form*(prototype) }}调用任何自定义标记即可。 如果集合更复杂(每个集合项包含多种类型),则上面的示例显示了如何自定义每个嵌入式类型。 - ptlis
我猜我们不在同一页上 :-) 我只是展示一个集合,没有添加更多的可能性。 $builder ->add('foobar', 'collection', array( 'type' => new ProductbundleProductType(), )); - user2382765
1
原则应该是一样的;如果ProductbundleProductType是一个简单的类型(例如类型扩展),那么"my_collection_item_widget"顶部的代码应该能够工作;如果它更加复杂,则应该使用代码块底部的重复部分(将"inner_widget"替换为ProductbundleProductType中每个类型的名称)。 - ptlis

1
我知道这是一个老问题,但也许人们仍然在遇到这个问题。这在集合的片段命名中有解释。
在这些情况下,您可以使用_entry_代替集合元素编号。请按照片段命名链接中的说明进行操作,但这可能会有所不同。有时,“类型”是片段名称的一部分,有时首字母大写,有时小写等。我建议使用浏览器开发工具查找实际名称以确保正确。您还可以通过将getBlockPrefix函数添加到表单类来自定义使用的名称。
因此,在上面的示例中,定制块可能如下所示:
{% block _ProductBundle_product_entry_widget %}
   <div> {{ form_row(form.field)}} </div>
{% endblock %}

“field”是您集合元素中字段的名称。”

1

这里有几点需要注意:

1. 文档错误

文档似乎与CamelCase实体有些出入。截至5.3版本,应该是:_taskManager_taskLists_entry_widget(而不是_task_manager_task_lists_entry_widget)。

您可以通过在模板中转储表单或表单字段:{{ dump(form) }}{{ dump(form.yourField) }},然后在vars部分查找unique_block_prefix来确认命名。

2. 不要使用块覆盖,宏更容易

这使得事情变得比必要的复杂。只需定义一个宏:

{% import _self as formMacros %}
{% macro formatCollection(form) %}
    <div class="form-group row">
        <div>
            {{ form_widget(form.field1) }}
            {{ form_widget(form.field2) }}
            {{ form_widget(form.field3) }}
        </div>
    </div>
{% endmacro %}

然后只需在每个集合上运行它:

{% for item in form.collectionItems %}
    {{ formMacros.formatCollection(item) }}
{% endfor %}

3. 原型

在渲染集合字段之前获取原型。您可以将其存储在变量中。

{% set prototype = form.collection.vars.prototype %}

然后只需使用我们的宏,随时渲染它:

<div data-js="collection" data-prototype="{{ formMacros.formatCollection(prototype)|e('html_attr') }}">

这让一切都更加清晰!非常感谢。 - SimonartM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接