Symfony form builder - how to put form title in h2 selector? - symfony

I want my form title to be displayed in h2 selector. I did something like that but it throws me an error "exception: "An exception has been thrown during the rendering of a template ("Notice: Undefined offset: -1").""
// how should I change THIS part? To change only the main form title/label?
// I made it work somehow but then it changes all labels... Is there some
// selector which allows to style MAIN title of the form?
{% block form_label %}
{% spaceless %}
<h2>{{ form_label(form) }}</h2>
{% endspaceless %}
{% endblock form_label %}
I've heard that I shouldn't put it in my project file in h2 selector just in form template. It's cleaner and even though documentation doesn't forbide that I was encouraged to do it another way and that's how I want to try it.
{% form_theme form 'Forms/base_form.html.twig' %}
{{ form_start(form) }}
{{ form_label(form, 'Project title', { 'label_attr': {'class': 'main-form-label'} }) }}
// so as I shouldn't put all that line in <h2> can I somehow do it in template between {% block form_label %} ?
{{ form_row(form.title, {'label': 'My title'}) }}
{{ form_row(form.isComplete, {'label': 'Dropdown'}) }}
{{ form_row(form.comment, {'label': 'Comment'}) }}
{{ form_row(form.submit, {'label': 'Submit'}) }}
{{ form_end(form) }}
Also... Whats the difference/what should I use - {% block form_label %} or {%- block form_label -%}
My whole template:
{% block form_label %}
{% spaceless %}
<h2>{{ form_label(form) }}</h2>
{% endspaceless %}
{% endblock form_label %}
{% block form_row %}
{% spaceless %}
{{ form_widget(form) }}
{{ form_errors(form) }}
{% endspaceless %}
{% endblock form_row %}
{% block submit_row %}
{% spaceless %}
<div class="col-12">
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock submit_row %}
{% block text_widget %}
{% spaceless %}
<div class="col-12">
<div>
{{ form_label(form) }}
</div>
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock text_widget %}
{% block choice_widget %}
{% spaceless %}
<span>
{{ form_label(form) }}
{% if expanded %}
{{ block('choice_widget_expanded') }}
{% else %}
{{ block('choice_widget_collapsed') }}
{% endif %}
{{ form_errors(form) }}
</span>
{% endspaceless %}
{% endblock choice_widget %}

In case you get stuck somewhere ;)
We use to do it this way (when we want custom rendering).
First, we extend the FormType :
namespace App\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
class FormTypeExtension extends AbstractTypeExtension
{
public function getExtendedType()
{
return FormType::class;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('main_title', false);
$resolver->setAllowedTypes('main_title', 'boolean');
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['main_title'] = $options['main_title'];
}
}
Think to register you extension in Symfony !
Then you can use it in your form builder this way :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(
'myField',
TextType::class,
array(
'main_title' => true
));
}
Finally, in your template :
{% block form_row -%}
{% spaceless %}
{% if main_title %}
<h2>{{ form_label(form) }}</h2>
{% else %}
{{ form_label(form) }}
{{ form_widget(form) }}
{% endif %}
{% endspaceless %}
{%- endblock form_row %}
Feel free to override that block with your needs ;-)

Related

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 %}

How to detect widget type in Symfony Twig rendering?

For rendering form rows, I need to detect what type of widget is being rendered. For example for checkboxes I want the label before the input, so I did this:
{% block form_row %}
{% spaceless %}
<div class="row">
{% if form.vars.block_prefixes[1] == "checkbox" %}
{{ form_widget(form) }}
{{ form_label(form) }}
{{ form_errors(form) }}
{% else %}
<div class="small-12 medium-3 columns">
{{ form_label(form) }}
</div>
{{ form_widget(form) }}
{{ form_errors(form) }}
{% endif %}
</div>
{% endspaceless %}
{% endblock form_row %}
I used form.vars.block_prefixes[1] to determine the widget it's about to render. Is that right? Or is there a better method? I can't seem to find it in the docs.
If you see symfony cookbook, you can find:
the block name is a combination of the field type and which part of the field is being rendered (e.g. widget, label, errors, row)
So, to customize checkbox form type rendering, you can define checkbox_widget block and checkbox_row for widgets order:
{% block checkbox_widget %}
{% spaceless %}
<label {% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>
{{ label }}
</label>
<input type="checkbox" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
{% endspaceless %}
{% endblock checkbox_widget %}
{% block checkbox_row %}
{% spaceless %}
{{ form_widget(form) }}
{{ form_errors(form) }}
{% endspaceless %}
{% endblock checkbox_row %}
If there is any data you need in the templates and symfony doesn't provide it by default you can always create a form type extension
A form type extension is a mechanism that let's you extend many form types in a single place. You can extend all form types provided you define the right extended type (FormType::class if you want to extend all forms).
This extension provides both the block_type for the current instance and the class name for all form types. This let's you use these variables in any generic template (such as widget_attributes).
I only tested it on symfony 3.4
<?php
namespace YourBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\FormType;
class FormTypeExtension extends AbstractTypeExtension
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
$type = $form->getConfig()->getType();
$inner_type = $type->getInnerType();
$view->vars = array_replace($view->vars, array(
'block_prefix' => $type->getBlockPrefix(),
'type_name' => get_class($inner_type)
));
}
public function getExtendedType()
{
return FormType::class;
}
}

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 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}) -}}

Symony2 Form Templating Error - Variable "widget" does not exist

I am trying to alter the date_widget using symfony2 and twig. I would like to display just the year.
After reading Symfony 2 date input with only year selector and the symfony2 book chapter on templating forms I have copied the date_widget block into my template and am receiving the error:
Variable "widget" does not exist in bundle....
here is the code from my template:
{% extends '::base.html.twig' %}
{% block body %}
{% block date_widget %}
{% spaceless %}
{% if widget == 'single_text' %}
{{ block('field_widget') }}
{% else %}
<div {{ block('widget_container_attributes') }}>
{{ date_pattern|replace({
'{{ year }}': form_widget(form.year),
})|raw }}
</div>
{% endif %}
{% endspaceless %}
{% endblock date_widget %}
....print form (generated though app/console generate:crud)
{% endblock %}
Any Thoughts? Thanks!
put the date_widget block outside of the body block, and add the following code after the initial extends
{% form_theme form _self %}
now, your code should look like this
{% extends '::base.html.twig' %}
{% form_theme form _self %}
{% block body %}
....print form (generated though app/console generate:crud)
{% endblock %}
{% block date_widget %}
{% spaceless %}
{% if widget == 'single_text' %}
{{ block('field_widget') }}
{% else %}
<div {{ block('widget_container_attributes') }}>
{{ date_pattern|replace({
'{{ year }}': form_widget(form.year),
})|raw }}
</div>
{% endif %}
{% endspaceless %}
{% endblock date_widget %}
http://symfony.com/doc/current/book/forms.html#form-theming

Resources