In Twig, is there a way to define a different theme for field errors as oppose to form related errors like CSRF error or composite unique constraints?
I want to display the field errors with a <span> and the form errors with a <ul>
{{ form_errors(form) }}
<table>
<tbody>
<tr>
<td>{{ form_label(form.tabla) }}</td>
<td>
{{ form_widget(form.tabla) }}
{{ form_errors(form.tabla) }}
</td>
<td></td>
<td>{{ form_label(form.descripcion) }}</td>
<td>
{{ form_widget(form.descripcion) }}
{{ form_errors(form.descripcion) }}
</td>
<td></td>
</tr>
</tbody>
</table>
This is my form theme that is being used for both cases (not what I want).
{% block field_errors %}
{% if errors|length > 0 %}
<span class="val-error">
{% for error in errors %}
{{ error.messageTemplate|trans(error.messageParameters, 'validators')~'. ' }}
{% endfor %}
</span>
{% endif %}
{% endblock field_errors %}
Is there a way to differentiate both cases?
Form class extends Field. If you set a new theme to field it will be applied to form.
You should override the field_errors block as you did, and then define the form_errors block using another template (with th ul tag).
{% block field_errors %}
{% if errors|length > 0 %}
<span class="val-error">
{% for error in errors %}
{{ error.messageTemplate|trans(error.messageParameters, 'validators')~'. ' }}
{% endfor %}
</span>
{% endif %}
{% endblock field_errors %}
{% block form_errors %}
{% if errors|length > 0 %}
<ul class="val-error">
{% for error in errors %}
<li>{{ error.messageTemplate|trans(error.messageParameters, 'validators')~'. ' }}</li>
{% endfor %}
</ul>
{% endif %}
{% endblock form_errors %}
Since field_* is removed in 2.3 the approved solution will no longer work. A hacky solution I have found is
{% block form_errors %}
{% if errors|length > 0 %}
{% if form.parent is empty %}
<ul class="val-error">
{% for error in errors %}
<li>{{ error.messageTemplate|trans(error.messageParameters, 'validators')~'. ' }}</li>
{% endfor %}
</ul>
{% else %}
<span class="val-error">
{% for error in errors %}
{{ error.messageTemplate|trans(error.messageParameters, 'validators')~'. ' }}
{% endfor %}
</span>
{% endif %}
{% endif %}
{% endblock form_errors %}
So basically what this does is if there is no parent, it knows it is the top level.
Related
[ Symfony 4 ]
I've this template code in Symfony:
{{ form_widget(registrationForm.username, {'attr': {'class': 'form-control'}}) }}
{{ form_errors(registrationForm.username) }}
Instead of just displaying error text, it's generating a list item like this:
<ul><li> Username already exists </li></ul>
How to not have it generate this list item and just get the text?
I guess it is correct behavior, cause you can have multiple errors for one field for example "Username is too short" and "Field Username contains inappropriate characters", but to get only first error you can use:
{{ form_errors(registrationForm.username|first) }}
Or you can customize your form_errors rendering, first create file for form_errors, for example your_form/custom_form_errors.html.twig :
{% block form_errors %}
{% spaceless %}
<div class="error">{{ errors|first }}</div>
{% endspaceless %}
{% endblock %}
And after that include it to your view file:
{% form_theme form 'your_form/custom_form_errors.html.twig' %}
...
{{ form_errors(registrationForm.username) }}
just to extend #Andrii Filenko 's answer. You can modify the output the form_errors or any other form twig function pretty easily. it's called custom theming in Symfony.
Consider this:
// templates/register.html.twig
{% extends "base.html.twig" %}
{% form_theme registrationForm _self %}
{% block form_errors %}
{% spaceless %}
{% if errors|length > 0 %}
<ul class="changed list">
{% for error in errors %}
<li>{{ error.message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endspaceless %}
{% endblock form_errors %}
{% block body %}{% endblock %}
Output:
<ul class="changed list"><li> Username already exists </li></ul>
I am developing a website and I would like to personalized how the forms are rendered.
My purpose is for each field I should achieve this structure
<div class="form-group">
<label for=":id">:LABEL</label>
<input type=":type" class="form-control" id=":id" />
</div>
In case of checkbox, the input must to appear first and without class atribute
The options for doing this are listed in Symfony Form Theming documentation.
In your form theme Twig file (normally app/Resources/views/form.html.twig) you should add something like this:
{% block form_row %}
{% spaceless %}
<div class="form-group">
{{ form_label(form) }}
{{ form_errors(form) }} {# Can remove this unless you want inline errors #}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
{% block form_widget_simple %}
{% spaceless %}
{% set type = type|default('text') %}
{% set attr = attr|merge({'class': (attr.class|default('') ~ ' form-control')|trim}) %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% endspaceless %}
{% endblock form_widget_simple %}
The Ryan answer has needed to add one more line like this:
{% form_theme form _self %}
{% block form_row %}
{% spaceless %}
<div class="form-group">
{{ form_label(form) }}
{{ form_errors(form) }} {# Can remove this unless you want inline errors #}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
{% block form_widget_simple %}
{% spaceless %}
{% set type = type|default('text') %}
{% set attr = attr|merge({'class': (attr.class|default('') ~ ' form-control')|trim}) %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% endspaceless %}
{% endblock form_widget_simple %}
as per symfony2 documentation How to customize Form Rendering the current template in which u want to make change , u will have to write the line
{% form_theme form _self %}
in the starting of any block overriding ..
I've overrided the Twig template for my forms in my symfony application so that I could have more control over the labels for checkboxes.
However, I am having problems with the checkbox label.
Here is the code I overrided in my custom template file:
{# Labels #}
{% block form_label %}
{% spaceless %}
{% if label is not sameas(false) %}
{% if not compound %}
{% 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 %}
{% set label = name|humanize %}
{% endif %}
{% endif %}
{% if 'checkbox' not in block_prefixes %}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ label|trans({}, translation_domain) }}</label>
{% endif %}
{% endspaceless %}
{% endblock form_label %}
{# Checkboxes #}
{% block button_label %}{% endblock %}
{% block checkbox_widget %}
{% spaceless %}
<label for="{{ id }}">
<input type="checkbox" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
{{ label }}</label>
{% endspaceless %}
{% endblock checkbox_widget %}
It works fine but I can't get the label text node for checkboxes working.
When I have a checkbox it generates something like:
<label><input type="checkbox"/></label>
Where it should be:
<label><input type="checkbox"/>Label Here</label>
Any clue on how to make the label string apear after the checkbox?
Edit:
I came to a solution, that worked pretty fine, but I am not sure if it the best.
{% block form_row %}
{% spaceless %}
<div>
{{ form_errors(form) }}
{% if 'checkbox' in block_prefixes %}
{{ form_widget(form) }}
{{ form_label(form) }}
{% elseif 'radio' in block_prefixes %}
{{ form_widget(form) }}
{{ form_label(form) }}
{% else %}
{{ form_label(form) }}
{{ form_widget(form) }}
{% endif %}
</div>
{% endspaceless %}
{% endblock form_row %}
You can remove the label in {% block choice_widget %} to prevent it from appearing in the default spot.
{% block choice_widget %}
{% spaceless %}
{% if expanded %}
<div {{ block('widget_container_attributes') }}>
{% for child in form %}
{{ form_widget(child) }}
{{ form_label(child) }} {# HERE #}
{% endfor %}
</div>
{% else %}
//....
{% endspaceless %}
{% endblock choice_widget %}
If you do so, you would have to override the {% block radio_widget %} too. Otherwise it won't have a label.
<label for="{{ id }}"><input type="radio" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />{{ label|trans }}</label>
You can then remove the {% if 'checkbox' not in block_prefixes %} line you put in {% block form_label %}.
It worked for me.
Symfony2 - How to put label and input for checkboxes/radios in a same line?
Edit :
it seems that they split the {% block choice_widget %} in 2.3.3. You have to edit the choice_expanded block now, and remove the label line as above.
{% block choice_widget_expanded %}
{% spaceless %}
<div {{ block('widget_container_attributes') }}>
{% for child in form %}
{{ form_widget(child) }}
{{ form_label(child) }} {# There #}
{% endfor %}
</div>
{% endspaceless %}
{% endblock choice_widget_expanded %}
Here are the Twig fields I'm trying to override: github
I have a list of checkbox that has been modified with this custom form theme:
{% block choice_widget_expanded %}
{% spaceless %}
<div {{ block('widget_container_attributes') }}>
{% for child in form %}
<div class="checkbox_row">
{{ form_widget(child) }}
{{ form_label(child) }}
</div>
{% endfor %}
</div>
{% endspaceless %}
{% endblock choice_widget_expanded %}
I tried adding a class using {{ form_row(form.fieldName), {'attr':{'class':'myclass'}}) }}
But that only adds a class to the div#formName_fieldName. Next I tried adding a custom block with
{% block _formName_fieldName_widget %}
{% spaceless %}
<input type="checkbox" {{ block('widget_attributes') }} value="{{ value }}" class="myclass_{{ value }}" />
{% endspaceless %}
{% endblock choice_widget_expanded %}
Notice I also included the value as I want to have myclass_1, myclass_2 etc.
The problem with this is that the block receives an array so it should be like this
{% block _fb_post_facebook_pages_widget %}
{% spaceless %}
<div {{ block('widget_container_attributes') }}>
{% for child in form %}
{{ form_widget(child) }}
{{ form_label(child) }}
{% endfor %}
</div>
{% endspaceless %}
{% endblock choice_widget_expanded %}
But then if I wanted to add the class to a specific checkbox, I would need to write something like this:
{% block _formName_fieldName_X_widget %}
{% spaceless %}
<input type="checkbox" {{ block('widget_attributes') }} value="{{ value }}" class="myclass_{{ value }}" />
{% endspaceless %}
{% endblock choice_widget_expanded %}
Where X would be a value that I have for the fieldName... And obviously the values could be anything so I can't just create block for all values from 1-1000000000000...
Any way to do this dynamically?
You would need to extend Twig, I believe. You can then specify the code in PHP and just bring in the conditions.
http://twig.sensiolabs.org/doc/advanced.html
Ok, bad question since semantically I think I can gather the difference by the block names themselves. My real question is how can I control which attributes appear a container and an element when widget_attributes and widget_containter_attributes are required on a given element.
Consider the following:
<div class="ui-select foo bar baz">
<select id="abc_hello_worldtype_name" name="abc_hello_worldtype[name]" class="thud grunt">
...
</select>
</div>
Main things I'm going after are having to set class names on both the div and the select. This is required for both style reasons as well as behavior-related requirements.
The main thing that is confusing me is that both the original widget_attributes and widget_container_attributes both use the attr variable passed in. Are these not intended to be used together?
I found myself doing something like the following today; just making my own blocks copied from the originals and adding to the conditionals. This all seems way too complicated. I know I'm doing it wrong.
{% block choice_widget_collapsed %}
{% spaceless %}
{% set attr = attr|merge({'class': (attr.class|default('') ~ ' ui-select')|trim}) %}
<div {{ block('ui_select_container_attributes') }}>
<select {{ block('ui_select_widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{% if empty_value is not none %}
<option value=""{% if required %} disabled="disabled"{% if value is empty %} selected="selected"{% endif %}{% endif %}>{{ empty_value|trans({}, translation_domain) }}</option>
{% endif %}
{% if preferred_choices|length > 0 %}
{% set options = preferred_choices %}
{{ block('choice_widget_options') }}
{% if choices|length > 0 and separator is not none %}
<option disabled="disabled">{{ separator }}</option>
{% endif %}
{% endif %}
{% set options = choices %}
{{ block('choice_widget_options') }}
</select>
</div>
{% endspaceless %}
{% endblock choice_widget_collapsed %}
Notice the ui_* block references on the div and the select. Those blocks look like:
{% block ui_select_widget_attributes %}
{% spaceless %}
id="{{ id }}" name="{{ full_name }}"{% if read_only %} readonly="readonly"{% endif %}{% if disabled %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %}
{% for attrname, attrvalue in attr %}{% if attrname in ['placeholder', 'title'] %}{{ attrname }}="{{ attrvalue|trans({}, translation_domain) }}" {% elseif attrname == 'class' %} class="foopa {{ attrvalue|replace({'ui-select':''}) }}" {% else %}{{ attrname }}="{{ attrvalue }}" {% endif %}
{% endfor %}
{% endspaceless %}
{% endblock ui_select_widget_attributes %}
{% block ui_select_container_attributes %}
{% spaceless %}
{% if id is not empty %}id="{{ id }}" {% endif %}
{% for attrname, attrvalue in attr %}{{ attrname }}="{{ attrvalue }}" {% endfor %}
{% endspaceless %}
{% endblock ui_select_container_attributes %}
When a form field is rendered as single form input (text input, select, checkbox...), widget_attributes is used. When it is rendered as a collection of inputs (form, multiple checkboxes, multiple inputs, ...), widget_container_attributes is used for the container surrounding the inputs (a div, mostly). So no, they are not intended to be used at the same time.
The difference between the two blocks is that widget_attributes renders form-specific attributes ("value", "name"...) while widget_container_attributes renders only generic HTML attributes.
If you want to add additional markup beyond the possibilities of the "attr" option, your best bet is to copy the corresponding block from the form theme (e.g. "choice_widget_collapsed"), paste it into your template, rename the block to match your element's ID with a leading underscore ("_") and a "widget" suffix (e.g. if your element's ID is "form_my_element", the block would be called "_form_my_element_widget") and modify the markup in your template.
{% block body %}
...
{{ form(form) }}
...
{% endblock %}
{% block _form_my_element_widget %}
... modified version of the "choice_widget_collapsed" markup ...
{% endblock %}