Symfony Twig Form Theming of specific fields - symfony

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

Related

Symfony form theme conflict

I have two forms in the same template page, where I need to apply for each form one specific theme. Unfortunately the first theme override the second one.
So that the second theme is not applied at all.
Another thing, in pages where I only have the form_subscribe form, the second theme is well applied for that form. The problems comes only when I have tow forms in the same page.
First Form:
{% form_theme form '#ezdesign/_form/bootstrap_full_form_theme.html.twig' %}
{{ form_start(form) }}
{{ form_end(form) }}
Second Form:
{% form_theme form_subscribe '#ezdesign/_form/bootstrap_modal_form_theme.html.twig' %}
{{ form_start(form_subscribe) }}
{{ form_end(form_subscribe) }}
It's not necessary to share my template content, anyway:
First theme templae:
{% extends 'bootstrap_4_layout.html.twig' %}
{% block form_row -%}
{%- if compound is defined and compound -%}
{%- set element = 'fieldset' -%}
{%- endif -%}
<{{ element|default('div') }} class="form-group">
{{- form_widget(form) -}}
</{{ element|default('div') }}>
{%- endblock form_row %}
Second Theme template:
{% extends 'bootstrap_4_layout.html.twig' %}
{% block form_row -%}
{%- if compound is defined and compound -%}
{%- set element = 'fieldset' -%}
{%- endif -%}
<{{ element|default('div') }} class="form-group">
<div class="row">
{{- form_label(form, null, {'label_attr': {'class' : 'col-sm-4'}}) -}}
<div class="col-sm-8">
{{- form_widget(form) -}}
</div>
</div>
</{{ element|default('div') }}>
{%- endblock form_row %}
Any idea would be appreciated and voted.
Have your registred your custom form templates in twig config ?
# config/packages/twig.yaml (symfony 4)
# app/config/config.yml (symfony < 4)
twig:
form_themes:
- ...
- '#ezdesign/_form/bootstrap_full_form_theme.html.twig'
- '#ezdesign/_form/bootstrap_modal_form_theme.html.twig'
https://symfony.com/doc/current/form/form_themes.html#applying-themes-to-all-forms
As this could well be a side effect of the caching, and as it seems to be linked to the fact that the two forms are in the exact same template, a way to fix this could be to separate your forms with includes:
some-page.html.twig
{{ include('partial/form.html.twig', { 'form': form }) }}
{{ include('partial/form-subscribe.html.twig', { 'form_subscribe': form_subscribe }) }}
partial/form.html.twig
{% form_theme form '#ezdesign/_form/bootstrap_full_form_theme.html.twig' %}
{{ form_start(form) }}
{{ form_end(form) }}
partial/form-subscribe.html.twig
{% form_theme form_subscribe '#ezdesign/_form/bootstrap_modal_form_theme.html.twig' %}
{{ form_start(form_subscribe) }}
{{ form_end(form_subscribe) }}
This way, you end up with only one form_theme per template, and wouldn't collide your themes as per this comment:
{# this form theme will be applied only to the form of this template #}
Source: https://symfony.com/doc/current/form/form_themes.html#applying-themes-to-single-forms

Symfony : easy way to format FORMS with CSS?

We're building a web site with Symfony 2. We generate a unique URL and send it by email to user who forgot their password, so they can reset their password.
We're building a simple form to reset a password. We have two labels ('Enter your new password' and 'Enter your new password again') with a textbox beside each.
We wanted the textbox to align with each other.
Lazy solution was to figure out two strings of the same length (!)
But I would have wanted to format them with CSS or put them in a table ...
Is that possible at all with Symfony's form ? I read documentation about customizing templates, but when we tried the solution proposed by Symfony's docs the widgets (textboxes) were not rendering ...
Here are some pics of the issue :
Crooked textboxes
Lazy solution
Here is the code of the twig where I think(!) the formatting should be done
{% block blockPrincipal_mp %}
<h1>{{ titre }}</h1>
{{ form_start(form) }}
<div class="containerForm">
<div class="error">
{{ form_errors(form) }}
</div>
{{ form_rest(form) }}
{{ form_end(form) }}
{% for flashMessage in app.session.flashbag.get('success') %}
<div class="confirm"><p> {{ flashMessage }}</p></div>
{% endfor %}
</div>
{% endblock %}
You can render all the different elements of the forms individually as opposed to just rendering it all at one time with form_rest(form) as you have in your example. form_rest() is going to render whatever hasn't been rendered yet. And up to this point, All that's been rendered are the errors.
I don't know what your form property's names are but here's an example:
{{ form_start(form) }}
<div class="form_errors">{{ form_errors(form) }}</div>
{# output all pieces of the username element individually #}
<div class="form_label">{{ form_label(form.username) }}</div>
<div class="form_input">{{ form_widget(form.username) }}</div>
<div class="form_errors">{{ form_errors(form.username) }}</div>
{# output all pieces of the password element individually #}
<div class="form_label">{{ form_label(form.password) }}</div>
<div class="form_input">{{ form_widget(form.password) }}</div>
<div class="form_errors">{{ form_errors(form.password) }}</div>
{{ form_rest(form) }}
{{ form_end(form) }}
This way you can control what HTML wrappers surround each piece of your form elements.
Note that you can also output the username and password fields by doing...
{{ form_row(form.username) }} {{ form_row(form.password) }}
...and it will still output the label, widget and errors but will use the default layout for those form types that is defined in your twig templates. So you have more control of rendering the parts if you do them individually.
This is great for custom forms and custom templates, however you can also override the default form element's layout if you want more control over how individual form elements are rendered throughout your site, by extending the form fields template.
https://symfony.com/doc/current/form/form_customization.html
The RepeatedType field can be dispayed separately:
{{ form_row(form.password.first) }}
{{ form_row(form.password.second) }}
or more controlled:
{{ form_label(form.password.first) }}
{{ form_widget(form.password.first) }}
{{ form_label(form.password.second) }}
{{ form_widget(form.password.second) }}

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

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