form_rest command showing previously-rendered Collection field - symfony

I have a form with a collection type field, rendered like that:
<div id="beneficiosTab" class="opcional">
Beneficios
<ul class="beneficios" data-prototype="{{ form_widget(formAtendimento.beneficios.get('prototype')) | e }}">
{% for beneficio in formAtendimento.beneficios %}
<li>{{ form_row(beneficio.coTipoBeneficio) }}</li>
<li>{{ form_row(beneficio.vrValor) }}</li>
<li>{{ form_row(beneficio.boConcedido) }}</li>
{% endfor %}
<li>Add Beneficio</li>
</ul>
</div>
<div style="clear:both"></div>
{{ form_rest(formAtendimento) }}
The form's entity can have multiple items of the collection, or none.
When the entity has items of the collection, it works fine, but when it has none, the "for" in the twig doesn't happen, and a "Beneficios" div is generated in form_rest.
Any way I can prevent that? Thanks in advance.

This seems like a bug in the form rendering. I managed to disable the extra form rendering in the form_rest function, by adding this code just after rendering the collection elements:
{% do form.uploads.setRendered() %}
Where "uploads" is my collection field type.
This doesn't seem like best practice to me though.
So the whole rendering looks like this:
<div id="uploads" data-prototype="{{ form_widget(form.uploads.vars.prototype)|e }}">
{% for upload in form.uploads %}
{{ form_widget(upload) }}
{% endfor %}
</div>
{% do form.uploads.setRendered() %}

form_rest generate all unrendered forms. Every input is form in Symnfony2, the same goes with collection type.
You never printed out collection, so Symfony makes it for you.
If you want hide it, and still use form_rest simply print it it to:
<div style="display: none" />

Related

Drupal 8 remove style element in views custom text

I encounter a problem with the way Drupal renders Global Custom text.
I need to use Custom text field in my view to wrap fields. The body field has some "style" element inside the HTML but they are removed.
{% if field_titre_rubrique is defined and field_titre_rubrique|length %}
<div class="ancre" id="{{ field_ordre_rubrique }}">
<h1 class="ancreMenu">{{field_titre_rubrique}}</h1>
<div>
{% if body is defined %}
{{body}}
{% endif %}
{% if field_pdf is defined %}
{{field_pdf}}
{% endif %}
</div>
</div>
<div>{{ edit_node }}</div>
{% endif %}
Do I have a solution to keep "style" elements ?
You can use {{ body|raw }} but be sure that you can do this safely.
More about this topic: https://www.drupal.org/node/2296163

Iterate lists/content in block template twig Drupal 8

How would I be able to supersede the hierarchical dependencies in Drupal 8's twig engine to be able to loop within the i.e Lists/Views which is assigned to a block. So we would have a template: block--views-block--[machine-name]-1.html.twig You will be required to have the variable {{ content }}
Which then recursively buries itself down to field templates. Its completely killing me that one would need so many levels to produce on block of content.
I would like to iterate within the top custom block template the list.
Attempted
{% for key, value in _context %}
<li>{{ key }}</li>
{% endfor %}
To evaluate what is available to iterate down into the object but with no luck. I did though find a nice overriding object structure to reach the field attributes but that was within the field level
item.content['#item'].entity.uri.value
Thanks
i use this to "generate" a picture from my
node--news--full.html.twig
<div class="col-md-3">
{{ content.field_newsbild }}
</div>
the twig debug suggests some filenames. i took this:
field--node--field-newsbild--news.html.twig
and in there i wrote:
{% for item in items %}
<img alt="" src="{{ file_url(item.content['#item'].entity.uri.value) }}" class="img-responsive" {{ attributes }} >
{% endfor %}
hope i'll help a bit.

CSS for Symfony2 forms: "Hello World" equivalent

How to alter the fields.html.twig template?
I am only looking for the "hello world" equivalent to see the first ever change to the form.
What's working (within a bundle view as add.Article.html.twig):
<div class="well;">
<form method="post" {{ form_enctype(form) }}>
{{ form_widget(form,{attr: {class:'test'}} ) }}
<input type="submit" class="btn btn-primary" />
</form>
</div>
The test class is only doing a CSS .test{background-color:grey;}. I am normally using Bootstrap.
In fields.html.twig I have:
{% block aliquam_input %}
{% spaceless %}
<div>
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form, { 'attr': {'class': 'test'} }) }}
</div>
{% endspaceless %}
{% endblock %}
and in app/config/config.yml:
# Twig Configuration
twig:
//..
form:
resources:
- 'AliquamSummaryBundle:Form:fields.html.twig'
The fields.html.twig is rendered ok to the main twig as I get an error when removing one of the hash in the fields twig.
What's not working (i.e. class test doesn't show) is when I try to make the fields.html.twig take effect on Article.html.twig:
{% block aliquam_input %}
{% spaceless %}
<div class="well;">
<form method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<input type="submit" class="btn btn-primary" />
</form>
</div>
{% endspaceless %}
{% endblock %}
As all forms will have the same format, I'd prefer to not set all html attributes to every single line. But what is the way to get the class "test" taking effect from the fields.html.twig template?
___________________________ Update for David _____________
Thanks #David, but unfortunately adding the name as form.name doesn’t change anything. The CSS attribute is still not transferred to the form in html. Well spot for the semicolon after well; it was a late night typo, but not related to the problem.
Here is what I have figured so far. I might have to end up having to enter bootstrap CSS to each individual row in a form (horrible thought) or figure how to do this by entities options, which comes almost to the same horrible thought as doing it for every row. Since the doc offers to enter a template for all forms via app/config/config.yml there should be a far simpler way, but I don’t get it.
The below is the only direction so far which made CSS work a little, i.e. class .test{background-color: black;} is doing its job:
{# ..\addArticle.html.twig #}
<div class="well">
<form method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<input type="submit" class="btn btn-primary" />
</form>
</div>
NB: there is no {% block .. %} around the div, which makes sense as I put the fields.html.twig to be included to the symphony standard widgets through app/config/config.yml. The above goes with the fields.html.twig below.
{# ..\fields.html.twig #}
{% block widget_attributes %}
{% spaceless %}
{{" class=\'test\'" }}
{% endspaceless %}
{% endblock widget_attributes %}
So far only fields with textarea show black color, bit it’s kind of “hello world”. I am not sure if this is right way to proceed as I have not found anything alike in this and other forums and the doc only states to look at form_div_layout.html.twig on git to decide which attribute to change, which is not really helpful (to me).
First change your well; class to well in your add.Article.html.twig.
In order to put a class on your widget I think you can try
{{ form_widget(form.name, {'attr': {'class': 'test'}}) }}
The difference with your code is the form.name parameter. If you only give the form it will not apply passed options. See the doc
Changing the fields.html.twig as
{# ..\fields.html.twig #}
{% block widget_attributes %}
{% spaceless %}
{{" class=\'test\'" }}
{% endspaceless %}
{% endblock widget_attributes %}
is indeed correct in what concerns Symfony and the reason that with bootstrap-CSS only the textareas show the class="test" is that bootstrap assigns mandatorily (all other than textareas) with the bootstrap specific one.
As I don't want to alter the bootstrap as such I ended up assigning the attributes per widget from within the entities - anything else would probably opening a can of worms.

Symfony Twig customize an Individual field for a collection

In the documentation there is a way in Symfony to customize a Individual field, based on the name/id of the widget.
{% form_theme form _self %}
{% block _product_name_widget %}
<div class="text_widget">
{{ block('field_widget') }}
</div>
{% endblock %}
{{ form_widget(form.name) }}
Here, the _product_name_widget fragment defines the template to use for the field whose id is product_name (and name is product[name]).
This works for a normal widget, but not if a widget is inside a collection. Because of the extra columns. Like this:
name="productbundle_product_type[foobar][1][value]" id="productbundle_product_type_foobar_1_value"
What is the way to make the Twig customization work inside the collection?
I thought something like this, but that doesn't work:
{% for db in edit_form.list %}
{% block _productbundle_product_type_foobar_{{ db.name }}_widget %}
<div class="text_widget">
{{ block('field_widget') }}
</div>
{% endblock %}
{% endfor %}
Even the following doesn't work:
{% _productbundle_product_type_foobar_1_value_widget %}
What is the way to make it work?
I was working on a project a couple of evenings ago and encountered precisely this problem - the solution I found is a pair of blocks that look like this (stripped of project-specific code):
{# 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 %}
I know this is old question, but maybe people still happening upon this. This is explained fragment naming for collections.
You use _entry_ in these cases in place of the collection element number. Use the instructions in the link for fragment naming, but this might vary. Sometimes 'type' is part of the fragment's name, sometimes first letter is upper case, sometimes lower case, etc. I would use a browser developer tool to find the actual name to make sure. You might also be able to customize the names used by adding the getBlockPrefix function to the form class.
Therefore, in your case above, the customized block might look something like:
{% block _ProductBundle_product_entry_widget %}
<div> {{ form_row(form.field)}} </div>
{% endblock %}
Where 'field' would be the name of a field in the element of your collection.
A few things here:
1. Documentation Error
The docs seem to be a bit off with CamelCase entities. As for 5.3 it should be: _taskManager_taskLists_entry_widget (instead of _task_manager_task_lists_entry_widget)
You can confirm the naming by dumping the form or form field: {{ dump(form) }} or {{ dump(form.yourField) }} in your template and then look for unique_block_prefix within the vars section.
2. Do NOT use the block overrides, macros are much more easy
It makes things absolutely more complicated than necessary. Simply define a macro:
{% 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 %}
then simply run it on each collection:
{% for item in form.collectionItems %}
{{ formMacros.formatCollection(item) }}
{% endfor %}
3. Prototype
Get the prototype before rendering the collection field. You can store it in a variable.
{% set prototype = form.collection.vars.prototype %}
Then simply render it whenever you like using our macro:
<div data-js="collection" data-prototype="{{ formMacros.formatCollection(prototype)|e('html_attr') }}">

How are labels on ArrayCollections in forms rendered?

I have a Customer object, which has many Emails.
I'm building a form for my customer, and I've added his emails as a collection. In my template, I render the emails portion like this:
<h4>Emails</h4>
{% for email in form.emails %}
<li>
{{ form_row(email.addr) }}
{{ form_row(email.isPrimary) }}
</li>
{% endfor %}
...
{{ form_rest(form) }}
This works fine, except if the customer has no emails. Then, form_rest() renders the label 'emails' at the bottom of my template.
Why does this only get rendered when form.emails is empty? How can I customize it? (Note I've already customized my label rendering for other form elements, and I don't want it to be the same for these 'collection labels'.)
I usually solved this problem this way:
{% for email in form.emails %}
{# ... #}
{% else %}
{{ form_widget(form.emails) }}
{% endfor %}
Unless someone suggests a better way of doing this.

Resources