Symfony form theme conflict - symfony

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

Related

Form Themes. What differ functions: {{ form_label(foo) }} vs {{ block('form_label') }}?

In form themes once there is such notation:
{%- block form_row -%}
{{- form_label(form) -}}
{{- form_errors(form) -}}
{{- form_widget(form) -}}
{%- endblock form_row -%}
and once such:
{%- block number_widget -%}
{{ block('form_widget_simple') }}
{%- endblock number_widget -%}
The block() function evokes a block, so what form_widget(form) function does?
The function is using a parameter (that is form), while the block is not. So, you can't use {{ block('form_label') }} to render a specific label for a form, but you can use it in another block (where likely you want to customize somehow all your labels)

Symfony2, a label Token appears when using form themes

I'm using Symfony2.6 , and I'm trying to customize form rendering. The problem that when I put {{ form_rest(form) }} into the form , a label Token appears. How can I make it hidden ?
This is the form theme
{% extends 'form_div_layout.html.twig' %}
{% block form_widget_simple %}
<div class="form-group">
{{ form_label(form, null, {'label_attr': {'class': 'control-label'}}) }}
{{ parent() }}
</div>
{% endblock %}
When I remove the block {% block form_widget_simple %} to test what gives , the Token label become hidden.
Edit:
I'd like to know also if it's correct to change the simple widget block and render inside it a label or no.
You've changed the block of the simple widget which shouldn't render a label (and it doesn't by default). If you really need to do it this way, you may check the type variable and do not render label for the hidden type. Something as following:
{% block form_widget_simple %}
<div class="form-group">
{% if type != 'hidden' %}
{{ form_label(form, null, {'label_attr': {'class': 'control-label'}}) }}
{% endif %}
{{ parent() }}
</div>
{% endblock %}
This works fine :
{% extends 'form_div_layout.html.twig' %}
//........
{%- block hidden_row -%}
<div style="display:none">
{{ form_widget(form) }}
</div>
{%- endblock hidden_row -%}

Defining custom twig form block for errors rendering

I'm trying to define a specific new block for form field errors rendering, keeping form_errors unchanged for common errors rendering.
# Twig Configuration
twig:
debug: %kernel.debug%
strict_variables: %kernel.debug%
form:
resources:
- 'ApplicationMyBundle:Main:form/customFormTheme.html.twig'
In customFormTheme.html.twig I overwrite a few blocks copied from form_div_layout.html.twig plus I added the folloowing new one.
{% block field_errors %}{% spaceless %}
{% if errors|length > 0 %}
<ul class="errors">
{% for error in errors %}
{% if error.messageTemplate|length %}
<li class="error">{{ error.messageTemplate|trans(error.messageParameters, 'validators') }}</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
{% endspaceless %}{% endblock %}
Then I expect to be able to use this block in my views like this :
<div>
{{ form_label(form.message, 'message.label'|trans({},'contact')|raw ) }}
{{ form_widget(form.message, {attr: {maxlength:1000, size:1000, rows:8}}) }}
{{ field_errors(form.message) }}
</div>
but I receive the following error :
The function "field_errors" does not exist. Did you mean "form_errors"
I also tried by naming my block text_errors or textarea_errors mentioned here but I haven't been luckier.
Any idea ?
Actually it works by defining the block text_errors or textarea_errors only and still use {{ form_errors(field.name) }} in your template. If a block named after the type of your field exists (according to form field types) it will be used instead of form_errors.
!! But you can't use directly {{ text_errors(field.name) }} in your twig template !!
The same way you can have a custom row for a specific type like this
{% block textarea_row %}{% spaceless %}
<div class="textarea l-field {{ (form_errors(form)?'error':'') }}">
{{ form_label(form) }}
{{ form_widget(form) }}
{{ form_errors(form) }}
</div>
{% endspaceless %}{% endblock textarea_row %}
and use it in your template as follow :
{# message has textarea field type #}
{{ form_row(form.message, {
label: 'message.label'|trans({},'contact')|raw ,
attr: {maxlength:1000, size:1000, rows:8}})
}}
You can also pass many custom parameters by using the object attr{}
{% block form_row %}
{% spaceless %}
<div class="form-field {{ (form_errors(form)?'error':'') }}">
{{ form_label(form) }}
{{ form_widget(form) }}
{{ dump(attr) }}
{% if attr.help is defined and not attr.help == '' %}<p class="form-help">{{ attr.help }}</p>{% endif %}
{{ form_errors(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
and use it like this
{{ form_row(form.message, {
label: 'message.label'|trans({},'contact')|raw ,
attr: {
maxlength:1000, size:1000, rows:8,
help: 'password.help'|trans({})|raw
}
})
}}

How to set a css class of the widget type with Symfony2

Is there a way to apply a specific class on the root div of an input depending on the input type ?
ex.:
<div class="radio-input">
<input...
....
</div>
I tried to overload "fields.html.twig" with this :
{% block form_row %}
{% spaceless %}
{% set type = type|default('text') %}
<div class="{{ type }}-input">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
But "type" is always equal to "text".
Not a particularly nice solution but it seems block_prefixes is passed to block form_row. Block_prefixes is an array of the form field types in order of inheritance so a text field called first name would have a block_prefixes array like:
block_prefixes = array("form","field","text","first_name")
Which means you could potentially get the second from last array element and use that:
{% block form_row %}
{% spaceless %}
<div class="{{ (block_prefixes|slice((block_prefixes|length - 2),1))|first }}-input">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
I'm sure there is a nicer way than this though I just don't have it to mind.
P.s type is undefined which is why its always text for you as you are setting it to default to text

How to add a css class on a "form_row" in twig template

I would like to know how I can add a css class on a "{{ form_row() }}" in twig. For the moment, I have this code :
{{ form_row(form.username, {'label' : "Login", 'attr': {'class': 'loginForm'}}) }}
... But the CSS class "loginForm" isn't used in the HTML code.
Thank you :) !
If you want the common class for the form_row (it means one class for form_label, form_widget and form_errors), you should customize a field_row block.
This article explains how to customize form fields: How to customize Form Rendering.
There are some methods to do this.
For example I'm using Method 2 (How to customize Form Rendering: Method 2):
{% extends 'form_div_layout.html.twig' %}
{% block field_row %}
{% spaceless %}
{% set class='' %}
{% if attr.class is defined %}
{% set class = 'class="' ~ attr.class ~ '"' %}
{% endif %}
<div {{ class }} >
{{ form_label(form, label|default(null)) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock field_row %}
My answer is very similar to nni6's but it allows you to pass through the entire attr array.
My HTML structure is for Twitter Bootstrap but you can have whatever you want. This example also places an error class on the wrapper div if there are any errors - this part is not required but is useful if you use Bootstrap:
{% extends 'form_div_layout.html.twig' %}
{% block field_row %}
{% spaceless %}
{% set field_row_classes = ['control-group'] %}
{% if errors|length > 0 %}
{% set field_row_classes = field_row_classes|merge(['error']) %}
{% endif %}
<div class="{{ field_row_classes|join(' ') }}">
{{ form_label(form, label|default(null)) }}
<div class="controls">
{{ form_widget(form, { 'attr' : attr|default({}) }) }}
{{ form_errors(form) }}
{% if help is defined %}
<p class="help-block">{{ help }}</p>
{% endif %}
</div>
</div>
{% endspaceless %}
{% endblock field_row %}
The difference here is that I'm calling form_widget with the attr array (if it was specified, defaults to {})
Now you can set up your form as normal but pass through a custom class:
{{ form_row(myentity.myproperty, { 'label' : 'mylabel', 'attr' : { 'class' : 'myclass' }}) }}
This post need an update !
Since Symfony 4.3, the row_attr option permits to add attributes (and so, some class). Let's check those links :
Here :
https://symfony.com/blog/new-in-symfony-4-3-more-form-improvements#row-attributes-in-form-themes
or more specifically : https://symfony.com/doc/current/reference/forms/types/form.html#row-attr
I use this code to configure bootstrap in symfony
{% block field_row %}
{% spaceless %}
<div class="control-group {% if errors|length > 0 %}error{% endif %}">
{{ form_label(form, label, { 'attr': { 'class': 'control-label' } }) }}
<div class="controls">
{{ form_widget(form, { 'attr' : attr|default({}) }) }}
{{ form_errors(form) }}
</div>
</div>
{% endspaceless %}
{% endblock field_row %}
The easiest way to accomplish this without overwriting blocks is this:
{% set attr = {'class' : form.myElement.vars.attr.class ~ ' addedClasses...', 'style' : form.myElement.vars.attr.style ~ '; addedStyles...'} %}
{{- form_row(form.myElement, {'attr' : attr}) -}}

Resources