如何在Symfony 2表单中自定义data-prototype属性

时间:2011-09-26 13:20:28

标签: forms collections symfony

从umpteens时代起,我就阻止了Symfony 2和表单的问题。

我有一种形式的网站实体。 “网站”是网站实体的集合,每个网站包含两个属性:“类型”和“网址”。

如果我想在我的数据库中添加更多的网站,我可以点击“添加其他网站”链接,这会在我的表单中添加另一个网站行。因此,当您单击提交按钮时,您可以同时添加一个或X个网站。

添加行的这个过程使用data-prototype属性,它可以生成网站子表单。

问题是我自定义我的表单以获得一个很棒的图形渲染......就像那样:

<div class="informations_widget">{{ form_widget(website.type.code) }}</div>
<div class="informations_error">{{ form_errors(website.type) }}</div>
<div class="informations_widget">{{ form_widget(website.url) }}</div>
<div class="informations_error">{{ form_errors(website.url) }}</div>

但数据原型并不关心这种自定义,HTML和CSS标签&amp;属性。我保留了Symfony渲染:

<div>
<label class=" required">$$name$$</label>
<div id="jobcast_profilebundle_websitestype_websites_$$name$$">
<div>
<label class=" required">Type</label>
<div id="jobcast_profilebundle_websitestype_websites_$$name$$_type">
<div>
<label for="jobcast_profilebundle_websitestype_websites_$$name$$_type_code" class=" required">label</label>
<select id="jobcast_profilebundle_websitestype_websites_$$name$$_type_code" name="jobcast_profilebundle_websitestype[websites][$$name$$][type][code]" required="required">
<option value="WEB-OTHER">Autre</option>
<option value="WEB-RSS">Flux RSS</option>
...
</select>
</div>
</div>
</div>
<div>
<label for="jobcast_profilebundle_websitestype_websites_$$name$$_url" class=" required">Adresse</label>
<input  type="url" id="jobcast_profilebundle_websitestype_websites_$$name$$_url" name="jobcast_profilebundle_websitestype[websites][$$name$$][url]" required="required" value="" />
</div>
</div>
</div>

有没有人有想法进行黑客攻击?

12 个答案:

答案 0 :(得分:72)

有点旧,但这是一个致命的简单解决方案。

这个想法只是通过Twig模板渲染集合项,因此您可以完全自定义将放置在data-prototype="..."标记中的原型。就好像这是一种正常的,通常的形式。

在yourMainForm.html.twig中:

<div id="collectionContainer"
     data-prototype="
         {% filter escape %}
             {{ include('MyBundle:MyViewsDirectory:prototype.html.twig', { 'form': form.myForm.vars.prototype }) }}
         {% endfilter %}">
</div>

在MyBundle中:MyViewsDirectory:prototype.html.twig:

<div>
    <!-- customize as you wish -->
    {{ form_label(form.field1) }}
    {{ form_widget(form.field1) }}
    {{ form_label(form.field2) }}
    {{ form_widget(form.field2) }}
</div>

信用:改编自https://gist.github.com/tobalgists/4032213

答案 1 :(得分:47)

我知道这个问题已经很老了,但是我遇到了同样的问题,这就是我的问题。我使用树枝macro来完成此任务。宏类似于函数,您可以使用不同的参数渲染它们。

{% macro information_prototype(website) %}
    <div class="informations_widget">{{ form_widget(website.type.code) }}</div>
    <div class="informations_error">{{ form_errors(website.type) }}</div>
    <div class="informations_widget">{{ form_widget(website.url) }}</div>
    <div class="informations_error">{{ form_errors(website.url) }}</div>
{% endmacro %}

现在您可以在任何地方渲染此宏。请注意,information_prototype()只是宏的名称,您可以根据需要为其命名。如果您想使用宏以相同的方式呈现给定的项目和原型,请执行以下操作:

<div class="collection" data-prototype="{{ _self.information_prototype(form.websites.vars.prototype)|e }}">
    {% for website in form.websites %}
        {{ _self.information_prototype(website) }}
    {% endfor %}
    <button class="add-collection">Add Information</button>
</div>

form.websites.vars.prototype包含您指定的prototype_name表单的原型数据。如果要在同一模板中使用宏,请使用_self.+macroname

您可以在Twig documentation

中找到有关宏的更多信息

答案 2 :(得分:25)

你可能已经发现了,但这是其他人的解决方案。

创建一个新模板并将此代码复制/粘贴到其中: https://gist.github.com/1294186

然后在包含您要自定义的表单的模板中,通过执行以下操作将其应用于表单:

{% form_theme form 'YourBundle:Deal:Theme/_field-prototype.html.twig' %}

答案 3 :(得分:5)

我知道答案很晚,但对访客来说可能很有用。

在您的主题文件中,您只需使用一个块来呈现网站小部件的每个集合条目,如下所示:

{% block _jobcast_profilebundle_websitestype_websites_entry_widget %}
     <div class="informations_widget">{{ form_widget(form.type.code) }}</div>
     <div class="informations_error">{{ form_errors(form.type) }}</div>
     <div class="informations_widget">{{ form_widget(form.url) }}</div>
     <div class="informations_error">{{ form_errors(form.url) }}</div>
{% endblock %}

还为您的集合小部件行创建主题块,如下所示:

{% block _quiz_question_answers_row %}
     {% if prototype is defined %}
        {%- set attr = attr | merge({'data-prototype': form_row(prototype) }) -%}
    {% endif %}

     {{ form_errors(form) }}

     {% for child in form %}
         {{ form_row(child) }}
     {% endfor %}
{% endblock %}

现在原型和渲染的集合条目将是相同的。

答案 4 :(得分:5)

我最近遇到了类似的问题。以下是如何覆盖集合原型而无需在html中明确设置它:

{% set customPrototype %}
    {% filter escape %}
        {% include 'AcmeBundle:Controller:customCollectionPrototype.html.twig' with { 'form': form.collection.vars.prototype } %}
    {% endfilter %}
{% endset %}
{{ form_label(form.collection) }}
{{ form_widget(form.collection, { 'attr': { 'data-prototype': customPrototype } }) }}

您可以在自定义树枝中随心所欲地做任何事情。例如:

<div data-form-collection="item" data-form-collection-index="__name__" class="collection-item">
<div class="collection-box col-sm-10 col-sm-offset-2 padding-top-20">
    <div class="row form-horizontal form-group">
        <div class="col-sm-4">
            {{ form_label(form.field0) }}
            {{ form_widget(form.field0) }}
        </div>
        <div class="col-sm-3">
            {{ form_label(form.field1) }}
            {{ form_widget(form.field1) }}
        </div>
        <label class="col-sm-3 control-label text-right">
            <button data-form-collection="delete" class="btn btn-danger">
                <i class="fa fa-times collection-button-remove"></i>{{ 'form.collection.delete'|trans }}
            </button>
        </label>
    </div>
</div>

当您只需要在特定地点执行此操作并且不需要适用于所有集合的全局覆盖时,此功能非常有用。

答案 5 :(得分:2)

我有一个类似的问题。你可能不得不调整这个以适用于你的情况,但有人可能会觉得它很有帮助。

创建新模板文件以保存自定义表单“主题”

./src/Company/TestBundle/Resources/views/Forms/fields.html.twig

通常,您可以使用 form_row 功能显示字段的标签,错误和小部件。但在我的情况下,我只是想显示小部件。正如您所说,使用数据原型功能也会显示标签,因此在我们新的fields.html.twig中键入您希望字段显示的自定义代码:

{% block form_row %}
{% spaceless %}
        {{ form_widget(form) }}
{% endspaceless %}
{% endblock form_row %}

我删除了容器div,标签和错误,然后离开了小部件。

现在在显示表单的twig文件中,只需在{%extends ...%}

之后添加
{% form_theme form 'CompanyTestBundle:Form:fields.html.twig' %}

现在,form_widget(form.yourVariable.var.prototype)只会呈现字段而不会显示任何其他内容。

答案 6 :(得分:1)

应用程序范围主题将应用于原型。 见Making Application-wide Customizations

答案 7 :(得分:1)

自定义数据原型的

Here is示例代码:

{{ form_widget(form.emails.get('prototype')) | e }}

其中emails - 你的收藏。

答案 8 :(得分:0)

要自定义不同的现有集合项VS原型,您可以像这样覆盖collection_widget:

{%- block collection_widget -%}
    {% if prototype is defined %}
        {%- set attr = attr|merge({'data-prototype': form_row(prototype, {'inPrototype': true} ) }) -%}
    {% endif %}
    {{- block('form_widget') -}}
{%- endblock collection_widget -%}

然后在您的自定义条目中:

{% block _myCollection_entry_row %}

  {% if(inPrototype is defined) %}
      {# Something special only for prototype here #}
  {% endif %}
{% endblock %}

答案 9 :(得分:0)

如果您不需要在系统范围内定义模板,只需在树枝模板中设置模板,然后让twig使用它。

{# using the profiler, you can find the block widget tested by twig #}
{% block my_block_widget %}
    <div >
      <p>My template for collection</p>
        <div >
          {{ form_row(form.field1)}}
        </div>
        <div>
          {{ form_row(form.field2)}}
        </div>
    </div>
{% endblock %}

{% form_theme form.my_collection _self %}

<button data-form-prototype="{{ form_widget(form.my_collection.vars.prototype) )|e }}" >Add a new entry</button>

答案 10 :(得分:0)

我为此添加了一个问题。我认为这是一个错误。

https://github.com/symfony/symfony/issues/41809

我还发现了一些与上述类似的解决方法 - 它们记录在我的 github 问题中。

答案 11 :(得分:-1)

本文重点介绍如何在树枝模板中使用预先存在的约定。

从Symfony Cookbook(http://symfony.com/doc/master/cookbook/form/form_collections.html)开始“如何嵌入表单集合”,您可以在数据原型中输入您希望的任何html_escaped表单数据(可能被视为黑客,但效果非常好) )并且只有使用该模板的页面才会发生变化。

在示例中,他们会告诉您:

    <ul class="tags" data-prototype="{{ form_widget(form.tags.vars.prototype)|e }}">
    ...
    </ul>

可以使用以下内容成功替换:

<table class="tags" data-prototype="&lt;tr&gt;  &lt;td&gt;&lt;div&gt;&lt;input type=&quot;text&quot; id=&quot;task_tags__name__tagId&quot; name=&quot;task[tags][__name__][taskId]&quot; disabled=&quot;disabled&quot; required=&quot;required&quot;    size=&quot;10&quot; value=&quot;&quot; /&gt;&lt;/div&gt;&lt;/td&gt; &lt;td&gt;&lt;div&gt;&lt;input type=&quot;text&quot; id=&quot;task_tags__name__tagName&quot; name=&quot;task[tags[__name__][tagName]&quot; required=&quot;required&quot; value=&quot;&quot; /&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;">
    <tr>
        <th>Id</th>
        <th>Name</th>
    </tr>
    <tr>
    ...pre existing data here...
    </tr>
</table>    

上面带有“tags”类的表的data-type属性是html-escaped版本(并且删除了换行符,但空格是正确且必需的):

<tr>
    <td><div><input type="text" id="task_tags__name__tagId" name="task[tags][__name__][taskId]" disabled="disabled" required="required"    size="10" value="" /></div></td>
    <td><div><input type="text" id="task_tags__name__tagName" name="task[tags[__name__][tagName]" required="required" value="" /></div></td>
</tr>

...但您还必须调整示例中的javascript以添加tr而不是li元素:

function addTagForm(collectionHolder, $newLinkTr) {
    ...
    // Display the form in the page in an tr, before the "Add a question" link tr
    var $newFormTr = $('<tr></tr>').append(newForm);
    ...
};

...

// setup an "add a tag" link
var $addTagLink = $('<a href="#" class="add_tag_link">Add a tag</a>');
var $newLinkTr = $('<tr></tr>').append($addTagLink);

...

对我来说,下一步是弄清楚如何在外部文件中定义原型,我可以在twig模板中以某种方式调用动态使用表单的数据原型。类似的东西:

<table class="tags" data-prototype="{{somefunction('App\Bundle\Views\Entity\TagsPrototypeInTable')}}">

所以如果其他帖子之一描述了这个并且我太密集了,或者如果有人知道怎么做,那就这样说吧!

有来自Francois的gitHub的链接,但我没有看到任何解释,所以我认为这可能是我将在未来几天中获得的更有活力的方法。

和平 史蒂夫

更新

还可以使用原型的一部分:

data-prototype="&lt;tr&gt;  &lt;td&gt;{{ form_row(form.tags.vars.prototype.tagId) | e }}&lt;/td&gt; &lt;td&gt;{{ form_row(form.tags.vars.prototype.tagName) | e }}&lt;/td&gt;&lt;/tr&gt;"

上面带有“tags”类的表的data-type属性是html-escaped版本(并且删除了换行符,但空格是正确且必需的):

<td>{{ form_row(form.tags.vars.prototype.tagId) | e }}</td>
<td>{{ form_row(form.tags.vars.prototype.tagName) | e }}</td>

(我使用了http://www.htmlescape.net/htmlescape_tool.html。)

当渲染页面时,Symfony将用{h}覆盖的{h}被动字段替换{{}}之间的信息(因为“| e”)。通过这种方式,字段级别的任何自定义都不会丢失,但是!您必须使用实体手动添加和删除原型字段:)