Symfony 2 Form Theming Each Row? - symfony

I have been over the docs many times, but I can not seem to find a simple answer to my question. In my form, how do I apply a themed block to each row of my form, so that the right classes can be applied.
To explain, I currently have this in my twig template:
{% form_theme form 'XXBundle:Themes/floatRows.html.twig' %}
{{ form_start(form, {'attr': {'id': 'MyIDForm'}}) }}
{{ form_errors(form) }}
{{ form_row(form.row1) }}
{{ form_row(form.row2) }}
{{ form_row(form.row2) }}
{{ form_end(form) }}
In my theme file:
{% block form_row %}
<div class="ftLeft">
{%- if form.parent is empty -%}
{{ form_errors(form) }}
{%- endif -%}
{{- form_label(form) -}}
{{- form_widget(form) -}}
{{- form_errors(form) -}}
</div>
{% endblock %}
However what I am trying to do is this,
In my theme file I want,
{% block FloatLeft %}
<div class="ftLeft">
{%- if form.parent is empty -%}
{{ form_errors(form) }}
{%- endif -%}
{{- form_label(form) -}}
{{- form_widget(form) -}}
{{- form_errors(form) -}}
</div>
{% endblock %}
{% block FloatRight %}
<div class="ftRight">
{%- if form.parent is empty -%}
{{ form_errors(form) }}
{%- endif -%}
{{- form_label(form) -}}
{{- form_widget(form) -}}
{{- form_errors(form) -}}
</div>
{% endblock %}
Then to be able to apply each of these blocks to each row in turn (or wherever else I want them):
{{ form_start(form, {'attr': {'id': 'MyIDForm'}}) }}
{{ form_errors(form) }}
{{ form_row(form.row1) }} <-Block FloatLeft
{{ form_row(form.row2) }} <-Block FloatRight
{{ form_row(form.row2) }} <-No Block, no float class!
{{ form_end(form) }}
All help all ways welcome.
Thanks.

I used to have the same problem. I just solved it by extending twig with new simple functions. If you don't know how to go to this topic
Your function and your template must have the same name.

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

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)

twig customizing form_row with form_theme

I want to remove 'div' from form_row block using form_theme like this:
{% form_theme feedback_form _self %}
{% block form_row %}
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
{% endblock %}
{{ form_start(feedback_form) }}
{{ form_row(feedback_form.name) }}
{{ form_row(feedback_form.email) }}
{{ form_row(feedback_form.subject) }}
<button type="submit">Send</button>
{{ form_end(feedback_form) }}
just like it is said in Doc https://symfony.com/doc/2.8/form/form_customization.html#customizing-the-form-row
The trouble is that twig tries to render the {% block form_row %} right in that place of template, where it is, and the result is
Variable "form" does not exist.
because I don't have variable 'form' in this template.
The template is rendered by separate action in the footer of page, and it doesn't extend anything.
Ok, I found the solution while was writing the question.
Putting the code of form_row block to the separate file solves the problem.
{# feedback_form.html.twig #}
{% form_theme feedback_form 'fields.html.twig' %}
{{ form_start(feedback_form) }}
{{ form_row(feedback_form.name) }}
{{ form_row(feedback_form.email) }}
{{ form_row(feedback_form.subject) }}
<button type="submit">Send</button>
{{ form_end(feedback_form) }}
{# fields.html.twig #}
{% block form_row %}
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
{% endblock %}
Extending another template helps too.
{# base.html.twig #}
{% block content %}
{% endblock %}
{# feedback_form.html.twig #}
{% extends 'base.html.twig' %}
{% form_theme feedback_form _self %}
{% block form_row %}
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
{% endblock %}
{% block content %}
{{ form_start(feedback_form) }}
{{ form_row(feedback_form.name) }}
{{ form_row(feedback_form.email) }}
{{ form_row(feedback_form.subject) }}
<button type="submit">Send</button>
{{ form_end(feedback_form) }}
{% endblock %}

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 apply class to form label?

I have this in a twig.
{{ form_errors(form) }}
{{ form_row(form.name, {'attr':{'class':'admin_finance_input'}}) }}
{{ form_row(form.amount, {'attr':{'class':'admin_finance_input'}}) }}
{{ form_rest(form) }}
Can anybody tell me how to set a class for form_label?
By checking the block_label from form_div_layout.html(https://github.com/symfony/symfony/blob/2.2/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig) you see this:
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ label|trans({}, translation_domain) }}</label>
{% endif %}
so you can do this
{{ form_row(form.name, {'label_attr ':{'class':'admin_finance_input'}}) }}
In addition of Joao answer,
You are able to configure class attribute (and other html attributes if wanted) also when decomposing a form row :
<div class='form_row'>
<div class="error">{{ form_errors(form.name) }}</div>
{{ form_label(form.name, 'Choose a name:', 'attr': {'class': 'admin_finance_label'}) }}
{{ form_widget(form.name) }}
</div>

Resources