How to customize the data-prototype attribute in Symfony 2 forms

后端 未结 11 1037
梦如初夏
梦如初夏 2020-12-12 11:35

Since umpteens days, I block on a problem with Symfony 2 and forms.

I got a form of websites entities. \"Websites\" is a collection of website\'s entities and each w

相关标签:
11条回答
  • 2020-12-12 12:20

    Application wide form theming will be applied to the prototype. See Making Application-wide Customizations

    0 讨论(0)
  • 2020-12-12 12:25

    If you do not need to define a template system-wide, simply set a template in your twig template, and ask twig to use it.

    {# 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>
    
    0 讨论(0)
  • 2020-12-12 12:28

    To customize differently existing collection items VS prototype, you can override collection_widget like this:

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

    And then in your custom entry:

    {% block _myCollection_entry_row %}
    
      {% if(inPrototype is defined) %}
          {# Something special only for prototype here #}
      {% endif %}
    {% endblock %}
    
    0 讨论(0)
  • 2020-12-12 12:29

    This post focuses on using pre-existing conventions within the twig template.

    Basing off "How to Embed a Collection of Forms" from the Symfony Cookbook (http://symfony.com/doc/master/cookbook/form/form_collections.html), you can just enter whatever html_escaped form data you wish in the data-prototype (maybe considered a hack, but works wonderfully) and only pages using that template will change.

    In the example, they tell you to put:

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

    This can be successfully replaced with something such as:

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

    Where the data-type attribute of the table with the class "tags" above is the html-escaped version (and line breaks removed though spaces are ok and required) of:

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

    ...but you must also adjust the javascript in the example to add tr's instead of li elements:

    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);
    
    ...
    

    For me, the next step is figuring out how to define the prototype in an external file that I can somehow call in the twig template for the data-prototype that dynamically works with the form. Something like:

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

    So if one of the other posts is describing this and I am too dense or if someone knows how to do such, say so!

    There is a link to something from gitHub from Francois, but I didn't see any explanation so I think that is probably the more dynamic method I'll get to one of these near-future days.

    Peace, Steve

    Update:

    One can also use just parts of the prototype:

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

    Where the data-type attribute of the table with the class "tags" above is the html-escaped version (and line breaks removed though spaces are ok and required) of:

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

    (I used http://www.htmlescape.net/htmlescape_tool.html.)

    Symfony will replace the information between the {{}} with an html_escaped (because of the "|e") rendered field when the page is rendered. In this way, any customization at the field-level is not lost, but! you must manually add and remove fields to the prototype as you do so with the entity :)

    0 讨论(0)
  • 2020-12-12 12:33

    I know this question is quite old, but I had the same problem and this is how I soved it. I'm using a twig macro to accomplish this. Macros are like functions, you can render them with different arguments.

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

    now you can render this macro wherever you want. Note that information_prototype() is just the name of the macro, you can name it whatever you want. If you want to use the macro to render the given items and the prototype the same way, do something like this:

    <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 holds the prototype data of the form with the prototype_name you specified. Use _self.+macroname if you want to use the macro in the same template.

    You can find out more about macros in the Twig documentation

    0 讨论(0)
提交回复
热议问题