I would like custom the render of form_errors in Symfony 3, because I would like obtain (in my Twig remplate) :
<div class="alert alert-danger">My error</div>
Instead of :
<ul><li>My error</li></ul>
It's possible ?
I tried this :
{{ form_errors(form.name) ? '<div class="alert alert-danger">' ~ form_errors(form.name) ~ '</div>' : '' }}
But the HTML is not interpreted and it may not be very clean ..
And this (Recovery of vendor\symfony\symfony\src\Symfony\Bridge\Twig\Resources\views\Form\form_div_layout.html.twig line 307):
I overloaded:
{%- block form_errors -%}
{%- if errors|length > 0 -%}
<ul>
{%- for error in errors -%}
<li>{{ error.message }}</li>
{%- endfor -%}
</ul>
{%- endif -%}
{%- endblock form_errors -%}
By :
{%- block form_errors -%}
{%- if errors|length > 0 -%}
<ul>
{%- for error in errors -%}
<li>{{ error.message }}</li>
{%- endfor -%}
</ul>
{%- endif -%}
{%- endblock form_errors -%}
But the variable "error" is not defined
Thanks in advance for your help
Take a look at this website:
https://symfonycasts.com/screencast/symfony-forms/form-theme-create#play
You need to modify below file
vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig
and search for the {% block form_errors -%} block to modify it.
you can look here for more informations:
https://codereviewvideos.com/course/beginner-s-guide-to-symfony-3-forms/video/styling-and-customising-using-form-fragments
But this is not the right method, the good one is to override this file by copy that block into another twig template, modify it and call this template into the twig template of your page.
Here are the explanations:
https://symfonycasts.com/screencast/symfony-forms/form-theme-create#play
Related
How to change default order of symfony form components?
I would like to have following order:
{{ form_label(form) }}
{{ form_widget(form) }}
{{ form_help(form) }}
{{ form_errors(form) }}
Currently is:
{{ form_errors(form) }}
{{ form_label(form) }}
{{ form_widget(form) }}
{{ form_help(form) }}
I have tried to modify this file: vendor/symfony/twig-bridge/Resources/views/Form/bootstrap_4_layout.html.twig:
...
{% block form_label -%}
{% if label is same as(false) -%}
{%- if errors|length > 0 -%}
<div id="{{ id }}_errors" class="mb-2">
{{- form_errors(form) -}}
</div>
{%- endif -%}
{%- else -%}
{%- if compound is defined and compound -%}
{%- set element = 'legend' -%}
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' col-form-label')|trim}) -%}
{%- else -%}
{%- set label_attr = label_attr|merge({for: id}) -%}
{%- endif -%}
{% if required -%}
{% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) %}
{%- endif -%}
{% if label is empty -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<{{ element|default('label') }}{% if label_attr %}{% with { attr: label_attr } %}{{ block('attributes') }}{% endwith %}{% endif %}>{{ translation_domain is same as(false) ? label : label|trans(label_translation_parameters, translation_domain) }}{% block form_label_errors %}{{- form_errors(form) -}}{% endblock form_label_errors %}</{{ element|default('label') }}>
{%- endif -%}
{%- endblock form_label %}
...
But without any success.
As you can see I have changed the order and put the errors in the first place, but still, it does not work and display an error in the first position.
First, you need to create a custom form theme for this, as outlined in the docs:
# config/packages/twig.yaml
twig:
form_themes: ['app_form_layout.html.twig']
Within app_form_layout.html.twig, we're going to override a couple of blocks to move the errors in the correct position:
We need to remove the default form_error(form) call from the form_label block, otherwise you'll end up with the form error being visible in two places.
We need to move that call to form_widget (which is a level up in the form template hierarchy tree)
Untested code example:
{% use "bootstrap_4_layout.html.twig" %}
{# this is basically the default form_label block, but with the form_error block definition removed, see line 260 of bootstrap_4_layout.html.twig #}
{% block form_label -%}
{# I think you can safely remove this if-statement, you currently might end up with 2 errors when you have a form with no label defined. #}
{% if label is not same as(false) -%}
{%- if compound is defined and compound -%}
{%- set element = 'legend' -%}
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' col-form-label')|trim}) -%}
{%- else -%}
{%- set label_attr = label_attr|merge({for: id}) -%}
{%- endif -%}
{% if required -%}
{% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) %}
{%- endif -%}
{% if label is empty -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<{{ element|default('label') }}{% if label_attr %}{% with { attr: label_attr } %}{{ block('attributes') }}{% endwith %}{% endif %}>
{%- if translation_domain is same as(false) -%}
{%- if label_html is same as(false) -%}
{{- label -}}
{%- else -%}
{{- label|raw -}}
{%- endif -%}
{%- else -%}
{%- if label_html is same as(false) -%}
{{- label|trans(label_translation_parameters, translation_domain) -}}
{%- else -%}
{{- label|trans(label_translation_parameters, translation_domain)|raw -}}
{%- endif -%}
{%- endif -%}
{# here was the form_errors call which is now gone #}</{{ element|default('label') }}>
{%- else -%}
{%- if errors|length > 0 -%}
<div id="{{ id }}_errors" class="mb-2">
{{- form_errors(form) -}}
</div>
{%- endif -%}
{%- endif -%}
{%- endblock form_label %}
{# here, we're adding the form_errors(form) call we just removed from the form_label block, so the error is in the correct position #}
{% block form_row -%}
{%- if compound is defined and compound -%}
{%- set element = 'fieldset' -%}
{%- endif -%}
{%- set widget_attr = {} -%}
{%- if help is not empty -%}
{%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%}
{%- endif -%}
<{{ element|default('div') }}{% with {attr: row_attr|merge({class: (row_attr.class|default('') ~ ' form-group')|trim})} %}{{ block('attributes') }}{% endwith %}>
{{- form_label(form) -}}
{{- form_widget(form, widget_attr) -}}
{{- form_help(form) -}}
{{- form_errors(form) -}} {# << here :-) #}
</{{ element|default('div') }}>
{%- endblock form_row %}
Is it possible to render an input field without id?
I would like to disable it in the twig file.
I want that because I have a error "non-unique id" in the chrome console.
I have conducted research using the Symfony's documentation and I have found something that should work: "Customizing Form Output all in a Single File with Twig"
The block I want to replace is
{%- block widget_attributes -%}
This block is inside of the form_div_layout.twig.html
I put this inside of my_form.twig.html
{% form_theme form _self %}
{% block form_row %}
{%- block widget_attributes -%}
id="{{ id }}" name="{{ full_name }}"
{%- if disabled %} disabled="disabled"{% endif -%}
{%- if required %} required="required"{% endif -%}
{%- for attrname, attrvalue in attr -%}
{{- " " -}}
{%- if attrname in ['placeholder', 'title'] -%}
{{- attrname }}="{{ translation_domain is same as(false) ? attrvalue : attrvalue|trans({}, translation_domain) }}"
{%- elseif attrvalue is same as(true) -%}
{{- attrname }}="{{ attrname }}"
{%- elseif attrvalue is not same as(false) -%}
{{- attrname }}="{{ attrvalue }}"
{%- endif -%}
{%- endfor -%}
{%- endblock widget_attributes -%}
{% endblock form_row %}
{{ form_start(form) }}
......
And I get the error:
"Variable "full_name" does not exist. "
I don't want to edit the form_div_layout.html.twig
I did it!
For now, I think I found the solution
{% form_theme form _self %}
{% block form_row %}
{% block input_widget %}
{% set id = random() %} {# Random number for the id #}
{{ block('form_widget') }}
{% endblock input_widget %}
{% endblock form_row %}
{{ form_row(form.my_form) }}
This is not the final solution because I want the "id" not to show in the input form.
I am fighting with customize on symfony forms for twig...
My problem is to add html inside a label for a radio button like <strong>25</strong> but it is escaped by default and I can't find how to unescape. I use the following code.
{% block choice_widget %}
{%
set labels = {
1: { title: '1 ' ~ 'label.credits'|trans ~ ' - <strong>1€</strong>' },
2: { title: '2 ' ~ 'label.credits'|trans ~ ' - <strong>2€</strong>' },
25: { title: '25 ' ~ 'label.credits'|trans ~ ' - <strong>25€</strong>' },
}
%}
<div {{ block('widget_container_attributes') }}>
{% for child in form %}
{% if child.vars.value matches '/^[1|2|25]{1}/' %}
{% set currentLabel = labels[child.vars.value].title %}
{% else %}
{% set currentLabel = '' %}
{% endif %}
{{ form_widget(child, {'label': currentLabel}) }}
{{ form_label(child) }}
{{ form_errors(child) }}
{% endfor %}
</div>
{% endblock %}
In your case you will need to override the form_label block and incorporate into your own theme (https://symfony.com/doc/current/form/form_customization.html), this is for bootstrap 4 (find the file in vendors folder called bootstrap_4_layout.html.twig, if you need this for bootstrap 3 just do the same but the file is bootstrap_3_layout.html.twig), notice where I've inserted the <strong> and </strong> tags:
{% block form_label -%}
{% if label is not same as(false) -%}
{%- if compound is defined and compound -%}
{%- set element = 'legend' -%}
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' col-form-label')|trim}) -%}
{%- else -%}
{%- set label_attr = label_attr|merge({for: id}) -%}
{%- endif -%}
{% if required -%}
{% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) %}
{%- endif -%}
{% if label is empty -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<{{ element|default('label') }}{% if label_attr %}{% with { attr: label_attr } %}{{ block('attributes') }}{% endwith %}{% endif %}><strong>{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}</strong>{% block form_label_errors %}{{- form_errors(form) -}}{% endblock form_label_errors %}</{{ element|default('label') }}>
{%- else -%}
{%- if errors|length > 0 -%}
<div id="{{ id }}_errors" class="mb-2">
{{- form_errors(form) -}}
</div>
{%- endif -%}
{%- endif -%}
{%- endblock form_label %}
You can try the above by putting it in the actual twig view and putting this statement above it:
{% form_theme form _self %}
Extra (for a checbkox): To achieve this for a checkbox you will need to override the label template, pull out the block for
{% block checkbox_radio_label -%} from bootstrap_4_layout.html.twig or bootstrap_3_layout.html.twig and then incorporate this into your own theme.
Extra extra (for a checkbox): If you also need to customise the widget, pull out the block for {% block checkbox_widget -%} i.e. remove the label rendering from within it (yes, for some internal reason the widget generates the label as well) and customise the way you want it to be. This way even calling form_label on a checkbox will output the label (otherwise form_label will not output anything)
Look here : http://symfony.com/doc/current/reference/forms/types/choice.html#select-tag-checkboxes-or-radio-buttons
You can put the parameter expanded to true and multiple to false in your formType for have radio buttons in your html
is it possible to add a violation to multiple paths? like:
$this->context->buildViolation($constraint->message)
->atPath('initialDate')
->atPath('finalDate')
->addViolation();
it only add to initialDate.
You can still add two violations with an empty message on the second
$this->context->buildViolation($constraint->message)
->atPath('phone')
->addViolation()
;
$this->context->buildViolation('')
->atPath('email')
->addViolation()
;
but you will have the error markup generated also in the second field
You can also override the form_errors block to adjust the markup if no message
{% block form_errors -%}
{% if errors|length > 0 -%}
{% if form.parent %}<span class="help-block">
{% else %}<div class="alert alert-danger">{% endif %}
<ul class="list-unstyled text-danger">
{%- for error in errors if error.message -%}
<li><span class="glyphicon glyphicon-exclamation-sign"></span>
{{ error.message }}
</li>
{%- endfor -%}
</ul>
{% if form.parent %}</span>{% else %}</div>{% endif %}
{%- endif %}
{%- endblock form_errors %}
Is it possible to have validation on the form but keep it on the field at that same time? I am trying to have all my errors display at the top of the page but I would also like to have a red border around the error field. Also, each of my errors on the top should have a link that gives focus on the field that has that error.
If I use {{ form_errors(form.title) }}, I don't access to {{ form_errors(form) }}. If I use error_bubbling => true on the field then I don't access to {{ form_errors(form.title) }}. I was thinking of building my errors display in my controller, but if I do that, I'm losing Twig's functionality.
Customize your form_row block to not display error
{% block form_row %}
<div class="form-group{% if errors|length == 1 %} has-error{% endif %}">
{{ form_label(form, label|default(null)) }}
{{ form_widget(form, { 'attr' : { 'class' : 'form-control' } } ) }}
</div>
{% endblock form_row %}
Customize form_errors to be clickable. You can create a jQuery function to listen for clicks on li. In the function get the id of the clicked li remove RelatedField to get the field id
{% block form_errors -%}
{% if errors|length > 0 -%}
<ul id="allErrors">
{%- for error in errors -%}
<li id="RelatedField{{ error.id }}">{{ error.message }}</a></li>
{%- endfor -%}
</ul>
{%- endif %}
{%- endblock form_errors %}
References
How to Customize Form Rendering - http://symfony.com/doc/current/cookbook/form/form_customization.html
Here is the solution that I'm currenly using, I don't know if it's a good one. Personnaly I would rather do it all in Twig but I couldn't find a better way.
DefaultController.php : I'm passing the $errors to my view.
$errors = array();
if ($request->getMethod() == 'POST') {
$form->bind($this->getRequest());
if ($form->isValid()) {
...
} else {
foreach ($form->all() as $child) {
if (!$child->isValid()) {
$errors[$form->getName().'_'.$child->getName()] = substr($child->getErrorsAsString(),7);
}
}
}
}
template.html.twig
{% if errors|length > 0 %}
<ul>
{% for key, error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
{% block form_widget_simple %}
{% spaceless %}
{% set type = type|default('text') %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %} {% if errors|length > 0 %}class="error"{% endif %} />
{% endspaceless %}
{% endblock form_widget_simple %}
script.js
$('.errorClick').click(function() {
$('#'+$(this).attr('data-id')).focus();
});