How are labels on ArrayCollections in forms rendered? - symfony

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.

Related

Issues with passing a variable to a macro

I have a menu block that shows 4 links. I'd like to remove the last one if the user has a specific role.
The menu block is created in a specific twig file, and inside a macro, as follows:
{% import _self as menus %}
{#
We call a macro which calls itself to render the full tree.
#see http://twig.sensiolabs.org/doc/tags/macro.html
#}
{{ menus.menu_links(items, attributes, 0) }}
{% set role = user.role %}
{% macro menu_links(items, attributes, menu_level, elements) %}
{% import _self as menus %}
{% if items %}
<span class='arrow-down'></span>
<ul{{ attributes.setAttribute('id', 'edit-profil-nav') }}>
{% for item in items %}
{% set item_classes = [
'col-xs-12',
'col-sm-12',
items|length == 3 ? 'col-md-4' : 'col-md-3',
item.in_active_trail ? 'active' : 'notactive',
] %}
<li{{ item.attributes.addClass(item_classes) }}>
{{ link(item.title, item.url) }}
</li>
{% endfor %}
</ul>
{{ attach_library('cnas/responsive-navigation') }}
{% endif %}
{% endmacro %}
The major issue I have is that I can't interfere with the macro: I'd like to be able to make comparisons with the user variable, but when I dump it while being in the macro, user shows null.
I found a lot of stuff while looking for an answer, but I've seen everything and its opposite, so I'd like to know if I can do that, and how
Thank you a lot!
macro's in twig have their own scope. You will need to pass the variable user as an argument towards the macro in order to access it.
You could also pass the special variable _context to the macro.
This variables holds all the variables you passed towards the template.

How to customize the submit button rendering?

I'm using symfony 2.4 and I need to set my own theme for just the submit button within a form. I was already able to customize the way input fields are rendered, the following code is working.
{% block form_row %}
{% spaceless %}
<div class="form-group">
{{ form_errors(form) }}
{{ form_label(form) }}
{{ form_widget(form, { 'attr': {'class': 'form-control'} }) }}
{% if help is defined %}
<span class="help">{{ help }}</span>
{% endif %}
</div>
{% endspaceless %}
{% endblock form_row %}
Question: Which block do I need to override to accomplish my goal?
As explained in the How to customize Form Rendering section of the documentation. The submit button related blocks that you should override in order to customise your form's button are {% block submit_widget %} and {% block button_widget %} depending on the level of customisation you want to introduce.
Check the form_div_layout.html.twig code to fully understand the default implementation of those helpers.
Good to know,
Buttons support in Forms added to Symfony 2.3.

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') }}">

Symfony Twig Form Theming of specific fields

I have a custom form field type and an associated form theme for it. On one page I have a lot of these fields, but one of the fields in particular I want to change.
Is there any way to theme certain fields of the same type (and in the same file) differently?
A simplified example:
form_fields.html.twig: (local theming file)
{% block my_dropdown_row %}
<div>
{{ form_label(form) }}
{{ form_widget(form) }}
{{ form_errors(form) }}
</div>
{% endblock %}
In my form template (all these fields have the same type - my_dropdown
{{ form_row(form.selectionA) }}
{{ form_row(form.selectionB) }}
{{ form_row(form.selectionC) }}
{{ form_row(form.final_selection) }}
How can I style the final field differently to the others? There is a lot of code in these widgets so less duplication the better.
This can be done. Here is how:
http://symfony.com/doc/current/cookbook/form/form_customization.html
Old question and already answered, but would be nice to have more detailed info.
{% form_theme form _self %}
{% block _product_name_widget %}
<div class="text_widget">
{{ block('form_widget_simple') }}
</div>
{% endblock %}
{{ form_widget(form.name) }}
Where block name _product_name_widget stands for
_ form type(block prefix)_ form item name_render function
http://symfony.com/doc/current/form/form_customization.html#how-to-customize-an-individual-field

Overwriting a field of AdmingeneratorGeneratorBundle

I am trying to overwrite a field of the AdmingeneratorGeneratorBundle and want to append a link to a certain field. That works quite well following the documentation:
{% extends_admingenerated "MyMainBundle:PageEdit:index.html.twig" %}
{% block form_status %}
{{ parent() }}
Preview
{% endblock %}
What I would need to do now, is to get the real id of my page instead of the static 8, but i could not figure out, what the object is called inside the twig template. Any ideas?
Update:
In my case, as I only need the ID of my page, I can use app.request.attributes.get('pk') to get it in twig. Would be interesting how to get other values tough.
Just use
{% extends_admingenerated "MyMainBundle:PageEdit:index.html.twig" %}
{% block form_status %}
{{ parent() }}
Preview
{% endblock %}
Cedric
The Documentation has been updated.
Thats ho it works:
{% extends_admingenerated "NamespaceYourBundle:List:index.html.twig" %}
{% block list_td_column_title %}
<span style="font-weight:bold">{{ Movie.title }}</span>
{% endblock %}

Resources