【问题标题】:How to customize the data-prototype attribute in Symfony 2 forms如何在 Symfony 2 表单中自定义 data-prototype 属性
【发布时间】:2011-11-25 05:21:28
【问题描述】:

自从无数天以来,我一直在阻止 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>

但是 data-prototype 并不关心这种自定义,它带有 HTML 和 CSS 标签和属性。我保留 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>

有没有人有想法来做这个黑客?

【问题讨论】:

    标签: forms collections symfony


    【解决方案1】:

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

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

    在您的MainForm.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

    【讨论】:

    • Endfilter twig 标签应该以一个大括号结束,而不是两个。 @Jivan 你能编辑它吗,因为我们不能只编辑一个字符的帖子吗?
    • 非常有帮助,谢谢!此外,这可能是常识,但如果您要走这条路,请不要在 twig 模板中包含 form_start 和 form_end 函数。我发现包含这些会导致持久性数据库出现问题的困难方式。
    • 不错的解决方案!谢谢!
    • 顺便说一句:我尝试使用包含的模板获得类似的结果,就像提供的原始原型一样。转义过滤器不会删除空格,也不会删除新行:gist.github.com/webdevilopers/… 我尝试添加 twig 的无空格但原型数据仍然不在一行中:gist.github.com/webdevilopers/…
    • 当实体已经在集合中有项目时,这是否也可以工作?对我来说,自定义模板仅适用于新条目
    【解决方案2】:

    我知道这个问题很老了,但我遇到了同样的问题,这就是我解决它的方法。我正在使用树枝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 中找到有关宏的更多信息

    【讨论】:

    • 我这辈子都无法让它发挥作用。你知道它对 Symfony2.5 是否仍然有效?
    • 这个答案应该被标记为最佳和最简单的答案!谢谢!
    • 我只得到“无法在字符串变量(“SendisPresentationFrontendBundle:DeliverySlip:deliverySlipForm.html.twig”)上调用方法(“widget_prototype”)。”有什么建议我能做些什么吗?
    • @MaxSchindler 您是否使用_self.widget_prototype 从同一文件中调用宏?
    • 此解决方案运行良好。请注意,从 Twig 2.0 开始,在 data-prototype 字段中使用 _self 会导致“无法在字符串变量上调用方法宏名”。要解决这个问题,您只需添加 {% import _self aswhatevername %} 并在模板中调用whatevername.macroname 即可。
    【解决方案3】:

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

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

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

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

    【讨论】:

    • 该模板非常适合迭代集合并根据需要呈现所有集合。谢谢
    • 不错!我什至想出了如何在不重复代码的情况下做到这一点:gist.github.com/eikes/5788231
    【解决方案4】:

    我最近遇到了类似的问题。以下是无需在 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】:

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

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

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

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

    【讨论】:

      【解决方案6】:

      我遇到了类似的问题。您可能需要调整它以适合您的情况,但有人可能会觉得它有帮助。

      创建一个新的模板文件来保存您的自定义表单“主题”

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

      通常,您可以使用 form_row 函数来显示字段的标签、错误和小部件。但就我而言,我只想显示小部件。正如您所说,使用 data-prototype 功能也会显示标签,因此在我们的新 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) 只会渲染该字段而不会渲染其他内容。

      【讨论】:

        【解决方案7】:

        应用范围的表单主题将应用于原型。 见Making Application-wide Customizations

        【讨论】:

          【解决方案8】:

          Here is 自定义数据原型的示例代码:

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

          emails 在哪里——你的收藏。

          【讨论】:

          • 如何定制?不是默认数据原型吗?
          【解决方案9】:

          要自定义不同的现有集合项 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 %}
          

          【讨论】:

            【解决方案10】:

            如果您不需要在系统范围内定义模板,只需在 twig 模板中设置一个模板,然后让 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>
            

            【讨论】:

              【解决方案11】:

              您可以针对两个块将自定义主题添加到集合字段:

              _myCollection_row_myCollection_entry_row

              _myCollection_row - 渲染整个集合。

              _myCollection_entry_row - 呈现单个项目。

              原型依赖于_myCollection_entry_row,因此如果您为_myCollection_row 设置主题,则只有您的主题会出现在表单中,而原型不会出现。原型使用_myCollection_entry_row

              所以最好先主题化_myCollection_entry_row,然后再主题化_myCollection_row(仅在需要时)。但是 - 如果您以 _myCollection_row 为主题,请确保它调用 _myCollection_entry_row 来呈现您收藏中的每个项目。

              【讨论】:

                【解决方案12】:

                这篇文章的重点是在 twig 模板中使用预先存在的约定。

                基于 Symfony Cookbook (http://symfony.com/doc/master/cookbook/form/form_collections.html) 中的“如何嵌入表单集合”,您可以在数据原型中输入您希望的任何 html_escaped 表单数据(可能被认为是 hack,但效果很好)和只有使用该模板的页面才会改变。

                在示例中,他们告诉您输入:

                    <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”类的表的数据类型属性是 html 转义的版本(尽管空格是可以和必需的,但删除了换行符):

                <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”类的表的数据类型属性是 html 转义的版本(尽管空格是可以和必需的,但删除了换行符):

                <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 将在呈现页面时将 {{}} 之间的信息替换为 html_escaped(由于“|e”)呈现的字段。这样,任何现场级别的定制都不会丢失,但是!您必须像使用实体一样手动向原型添加和删除字段:)

                【讨论】:

                • 在原型属性中添加硬编码的表单代码违背了 Symfony 动态生成表单的全部意义。您不想在添加或删除某些字段时更新原型。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-04-23
                • 1970-01-01
                相关资源
                最近更新 更多