Symfony2 twig forms - forms

Symfony2 uses some twig templates when rendering forms.
In particular, in order to render the choice form field collapsed, symfony2 uses the following snippet of code (from form_div_layout.html.twig):
{%- block choice_widget_collapsed -%}
{%- if required and placeholder is none and not placeholder_in_choices and not multiple -%}
{% set required = false %}
{%- endif -%}
<select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{%- if placeholder is not none -%}
<option value=""{% if required and value is empty %} selected="selected"{% endif %}>{{ placeholder != '' ? placeholder|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>
{%- endblock choice_widget_collapsed -%}
In my application, I want to create a new form type, that extends the default collapsed choice type, but where choice list has custom formatting.
In other words, for my form field type, named places_widget, extending choice_widget_collapsed block but redefining choice_widget_options.
I already tried:
/* My first attempt */
{%- block places_widget -%}
{% block choice_widget_options %}
{{ block('choice_places_widget_options') }}
{% endblock choice_widget_options %}
{{ block('choice_widget_collapsed') }}
{%- endblock places_widget -%}
/* My other attempt */
{%- block places_widget -%}
{{ block('choice_widget_collapsed', { 'choice_widget_options' => choice_places_widget_options }) }}
{%- endblock places_widget -%}
/* together with */
{%- block choice_places_widget_options %}
// code here
{%- endblock choice_places_widget_options -%}
None of the two solutions is working, and actually I have the feeling I am doing things in the wrong way.
Does anyone have any idea on how to achieve the goal?
Thank you in advance

Forms can be customized using themes (see the doc here: http://symfony.com/doc/current/cookbook/form/form_customization.html#form-theming-in-twig)
In your case, it seems you need to load your own theme with:
{% form_theme form _self %}
Note that you could externalize these blocks in another file (refer to http://symfony.com/doc/current/cookbook/form/form_customization.html#method-2-inside-a-separate-template to see how to do that)

Related

Grav CMS: how to access form labels from template (checkboxes)

With a form including a checkboxes field and an email or save action set up, is there a way to access the individual checkboxes' labels from within the template?
So far, I've only been able to access the ids and values of the checkboxes, like so:
{%- for field in form.fields -%}
{%- set value = form.value(field.name) -%}
{%- if field.type == "checkboxes" -%}
{%- for key in in value|keys -%}
{{- key ~ ": " ~ value[key] ~ "\r\n" -}}
{%- endfor -%}
{%- endif -%}
{%- endfor -%}
To get the default values and labels of a checkboxes field, you could try the following:
The field definition of the form:
fields:
...
myfield:
type: checkboxes
label: A 'checkboxes' field
default:
option1: true
option2: false
options:
option1: Option 1
option2: Option 2
use: keys
Inside Twig template:
{% for field in form.fields %}
{% if field.type == 'checkboxes' %}
{% for key, label in field.options %}
<p>{{ label ~ ': ' ~ (field.default[key] ? 'true' : 'false') }}</p>
{% endfor %}
{% endif %}
{% endfor %}
Result in browser:

How can I put the asterisk of my required field on my label? (Symfony form)

I am working on Symfony 3 and I have some trouble with my form.
When I create a Symfony form with a field not required, here is my code :
I create the form :
$form = $this->createFormBuilder()
->add('prenom' TextType::class, array(
'label' => 'Votre prénom',
'required' => false
)
->getForm();
Here is the code in my view for this field:
{{ form_label(form.prenom) }}
{{ form_errors(form.prenom) }}
{{ form_widget(form.prenom) }}
And this is the HTML I have :
<label class="control-label" for="contact_prenom">Votre prénom</label>
<input type="text" id="contact_prenom" name="contact[prenom]" class="form-control"/>
Now if I do the same without the 'require' => false on my FormBuilder, here is the HTML I get:
<label class="control-label required" for="contact_prenom">Votre prénom</label>
<sup class="required" title="Champ obligatoire">
<i class="fa fa-asterisk"></i>
</sup>
<input type="text" id="contact_prenom" name="contact[prenom]" required="required" class="form-control" />
Is it possible to control the "sup" tag so the asterisk * can be with my label?
I guess I can do it with jQuery, but I'd like to know if it is possible to do it on my form builder or in Twig?
In the doc there is a specific section here http://symfony.com/doc/current/form/form_customization.html#adding-a-required-asterisk-to-field-labels
You can even do with CSS only
label.required:before {
content: "* ";
}
As of Symfony 5.1 you can do the following
->add('name', TextType::class, [
'label' => 'Name <span class="badge badge-danger badge-pill">Required</span>',
'label_html' => true
])
The label_html (bool) property will allow HTML to be injected into the label directly and render on the form output.
Documentation - https://symfony.com/doc/current/reference/forms/types/form.html#label-html
Yes, you could override the twig template or the block that symfony uses to render your widget, have a look at:
http://symfony.com/doc/current/templating/overriding.html
In your case, you're looking for
vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
And this would be the block you want to override:
{%- block form_label -%}
{% if label is not same as(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 -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<label{% if label_attr %}{% with { attr: label_attr } %}{{ block('attributes') }}{% endwith %}{% endif %}>{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}</label>
{%- endif -%}
{%- endblock form_label -%}

Separated input text fields for single input field

Is there any Symfony2 option to show some separated text fields from one single text field in a form?
Try to use form customization feature. Please check this article for more information -http://symfony.com/doc/current/cookbook/form/form_customization.html
Example:
form_theme.twig.html
{% block file_widget %}
{% spaceless %}
<td>{% set type = type|default('file') %}
<input type="{{ type }}" {{ block('widget_attributes') }} />
</td>
{% endspaceless %}
{% endblock file_widget %}
template.twig.html
{% form_theme form 'MyBundle:Form:form_theme.html.twig' %}
{{ form_row(form.contract) }}

Symfony2 Theming form generated by embedded controller action

I am generating the form in embedded controller action. And now i have faced the following problem. The Form Theming is not working in that case.
So what i have:
The tempalte "page.html.twig"
{% block content %}
{% render 'MyBundle:Widget:index' %}
{% endblock %}
The indexAction() creates the form and rendering another template "form.html.twig" which is normally renders a form using form_row, form_rest and so on.
So, now i am trying to customize form theming, and here is my problem.
When i put the code
{% form_theme form _self %}
in the page.html.twig, i got an error the the form variable does not exists. And its correct, the form var is created later in the embedded controller.
But when i put the theming code in embedded template "form.html.twig", i got another error "Variable "compound" does not exist"
{% block form_label %}
{% spaceless %}
{% 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 %}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %} {% if attr.tooltip is defined %}title="{{ attr.tooltip }}"{% endif %}>{{ label|trans({}, translation_domain) }}{% if required %}<span>*</span>{% endif %}</label>
{% endspaceless %}
{% endblock form_label %}
This part of code was copied from this file https://github.com/symfony/symfony/blob/2.1/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
So tried someone to do something like this?
Answering my question myself.
It was a small sentence in Symfony2 docs http://symfony.com/doc/current/book/forms.html
This {% form_theme form _self %} functionality will only work if your template extends another. If your template does not, you must point form_theme to a separate template.
So there are two solutions to solve this problem:
move form theme code to the separate file and include it in a embedded template using
{% form_theme form with 'fields.html.twig' %}
leave the form theme code in the same template, where the form will be generated, but extend the template from some "form.html.twig" empty template.
I have only done the second way, and its works, but I am sure the first one will work as well.

Symfony2: add class to every select

I'm trying to customize Symfony2 form rendering to add a class to every select that is generated.
I thought that having a custom form_div_layout.html.twig with:
{% block choice_widget_collapsed %}
{% spaceless %}
<select class='myclass' {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{% if empty_value is not none %}
<option value="">{{ 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>
{% endspaceless %}
{% endblock choice_widget_collapsed %}
and using it with
{% form_theme form 'YOPYourOwnPoetBundle:Form:form_div_layout.html.twig' %}
would do the trick.
However, the class 'myclass' isn't added to the select.
What am I doing wrong?
You should first make sure the theme file you're trying to use has the same name as the name you're using in the form_theme expression and that the file really is there. I can't remember off top of my head whether Twig throws an exception or not in case these do not match.
In addition, you might be accidentally passing a class attribute either when building a form or rendering it. What happens is that your element now has two class attributes.
A workaround is to actually add your new class to the collection of existing ones.
{% block choice_widget_collapsed %}
{% spaceless %}
{% set label_attr = label_attr|merge({class: label_attr.class|default('') ~ ' required'}) %}
{% set attr = attr|merge({class: (attr.class|default('') ~ ' myclass')|trim}) %}
<select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{# ... #}
</select>
{% endspaceless %}
{% endblock choice_widget_collapsed %}
This allows you to add any optional class you might need for specific elements later on.
EDIT
Looking at the Sf2 Github repsitory it seems that the theme file has been recently changed. In versions 2.0.* you should be overriding choice_widget, in versions 2.1.* the correct block is choice_widget_collapsed.
I suppose you should either change the form theme line to:
{% form_theme form 'YOPYourOwnPoetBundle:Form:form_div_layout.html.twig' %}
or you need to change the name of your twig file into fields.html.twig
Both have to match.