I am creating custom Twig form template blocks to do some special rendering.
I have been unable, so far, to use some of the built-in form blocks. Specifically, and of the {{ *_widget() }} blocks.
Example:
Declare a new Twig function.
/* src/SiteBundle/Library/TwigExtension.php - Bundle-specific Twig extension */
...
public function getFunctions
{
$ret = [
new \Twig_SimpleFunction( 'wwui_myBlock', null, ['node_class'=>'Symfony\Bridge\Twig\Node\RenderBlockNode, 'is_safe'=>['html']] )
];
return $ret;
}
The extension is registered in the service.yml file and is obviously recognized since there are no errors about the wwui_myBlock function being undefined.
Define the Twig block.
{# src/SiteBundle/Resources/views/Form/fields.html.twig - Custom form blocks #}
{% block wwui_myBlock %}
<div class="formRow form-group-xs clearfix">
<div class="col-xs-1"></div>
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' col-xs-2' )|trim}) -%}
{{ form_label( form ) }}
<div class="col-xs-9">
{{ choice_widget( form ) }}
</div>
</div>
{% endblock wwui_myBlock %}
In the page template.
{# Page-specific template #}
...
{{ form_start( form ) }}
...
{{ wwui_myBlock( form.options ) }} {# options is a choice with radio buttons. #}
...
{{ form_end( form ) }}
When this page is rendered an exception is thrown:
The function "choice_widget" does not exist in SiteBundle:Form:fields.html.twig at line 62
My fields.html.twig file "uses" bootstrap_3_horizontal_layout.html.twig. That file "uses" bootstrap_3_layout.html.twig which, in turn, "uses" Twig's default form_div_layout.html.twig.
Why can't my custom block see choice_widget which is defined in form_div_layout.html.twig?
Turns out that the proper way to access such blocks is to use the block() function. so, instead of
{{ choice_widget( form ) }}
it's
{{ block( 'choice_widget', form ) }}
Related
I try to make a registration form in Symfony 3.4 but it makes me mad that I cannot place errors properly.
This is how I render the form itself:
{{ form_start(registrationForm) }}
{{ form_widget(registrationForm) }}
{{ form_end(registrationForm) }}
And the fields are rendered from a custom template, where I declare that errors should appear under the field:
{% block form_row %}
<div class="form-group">
{{ block('form_label') }}
{{ block('form_widget_simple') }}
{{ block('form_error') }}
</div>
{% endblock %}
However all my errors are appearing above the whole form as li elements in an ul element. I tried to add 'error_bubbling' => false but it won't make any difference. (The error messages come form the Assert annotations on the underlying model object.)
What am I doing wrong?
UPDATE/EDIT
This is what I have done, but for some reason its not working. So the services is as follows,
clearfix.extended_type:
class: Bundle\Form\ClearfixExtension
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType }
Now I am sure this is wrong, I have not used services much and still trying to get my head around them.
I ave also built the class, see answer below for anyone else reading this, with the right namespace.
I have also done the following to the block,
{%- block form_row -%}
<div class="other classes here {% if clearfix %}clearfix"{% else %}"{% endif %}>
{{- form_widget(form) -}}
{{- form_label(form) -}}
{{- form_errors(form) -}}
</div>
{%- endblock form_row -%}
But all I get from my form when I try to render it is, "Variable "clearfix" does not exist in....."
So what have I done wrong? I guessing it has something to do with the service setup?
Thanks
ok, this is doing my head in a little, has I have gone over and over the docs but I am either doing it completely wrong or its what I want to do is just not do able?
So I am using Symfony 2.7, and have it setup to point to a forms twig file, so that I can override some of the defaults. This is working fine without any problems. However I now want to be able to render some of the rows with an added clearfix class. This is what I am rending right now,
{%- block form_row -%}
<div>
{{- form_widget(form) -}}
{{- form_label(form) -}}
{{- form_errors(form) -}}
</div>
{%- endblock form_row -%}
That renders all my rows with the right classes, but I want to be able to use the following on some selected rows
{%- block _clerfix -%}
<div class="clearfix">
{{- form_widget(form) -}}
{{- form_label(form) -}}
{{- form_errors(form) -}}
</div>
{%- endblock _clerfix -%}
And then I call this with the block_name within the Abstract Type of the form that I want to build with the added clearfix class, like so,
->add('password', 'password', ['label' => 'Password',
'required' => false,
'block_name' => 'clearfix',
'attr' => ['autocomplete' => 'off','class' => 'Passwordd', 'aria-required' => 'true' ]
])
Is what I want doable? I don't really want to add that clearfix into the user form I am building now, as that will not keep my code very DRY, as there will be (one would think so) places where I will need to re-use that block to render other fields?
All help most welcome.
Thanks.
You're can create form extension for defining option to apply clearfix
Symfony 2.8
class ClearfixExtension extends AbstractTypeExtension
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['clearfix'] = $options['clearfix'];
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('clearfix', false);
}
public function getExtendedType()
{
return FormType::class;
}
}
services.yml
clearfix_extension:
class: Bundle\Form\ClearfixExtension
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType }
Symfony 2.7
class ClearfixExtension extends AbstractTypeExtension
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['clearfix'] = $options['clearfix'];
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('clearfix', false);
}
public function getExtendedType()
{
return 'form';
}
}
services.yml
clearfix_extension:
class: Bundle\Form\ClearfixExtension
tags:
- { name: form.type_extension, alias: form }
and check this options in twig
{%- block form_row -%}
<div {% if clearfix %}class="clearfix"{% endif %}>
{{- form_widget(form) -}}
{{- form_label(form) -}}
{{- form_errors(form) -}}
</div>
{%- endblock form_row -%}
I'm quite new to symfony2, i have managed to implement fosUserBundle and Braincrafted bootstrap bundle.
I am trying to style the registration form to use boot strap and the inputs with input-sm class
the username and email are displaying as input-sm but the 2 password fields refuse to resize to the class i have applied.
is there somewhere in the fosUserbundle where the password field widget is configured
{% trans_default_domain 'FOSUserBundle' %}
{{ form_start(form, { 'style': 'horizontal', 'col_size': 'xs', 'label_col': 5, 'widget_col': 7, attr: {class: 'pull-left'}}) }}
{{ form_row(form.username, { attr: {class: 'input-sm'}}) }}
{{ form_row(form.email, { attr: {class: 'input-sm'}}) }}
{{ form_row(form.plainPassword, { attr: {class: 'input-sm'}}) }}
{{ form_row(form.plainPassword.second, { attr: {class: 'input-sm'}}) }}
<div class="col-sm-5"></div>
<div class="col-sm-7">
<input class="btn-primary btn-sm" type="submit" value="{{ 'registration.submit'|trans }}" />
</div>
{{ form_rest(form) }}
{{ form_end(form) }}
FosUserBundle or not, you can overwrite Twig form rendering directly to achieve this, have a look at How to customize form rendering in SF cookbook.
Basically you can either get more precise in your template by splitting form_row into labels, widgets and errors to target the input ("form_widget" below) more specifically and apply the correct classes :
{{ form_errors(form.plainPassword) }}
{{ form_label(form.plainPassword) }}
{{ form_widget(form.plainPassword) }}
You can also generate your own form style for desired type of input by decalring it directly in your template :
{% form_theme form _self %}
{% block integer_widget %}
<div class="integer_widget">
// Your custom layout
</div>
{% endblock %}
{% Block body %}
// Rendering your form, your custom layout will be used.
{% endblock %}
You can also declare it in a separate template if you want to use it somewhere else, refer to the above cookbook page to see how.
Here's the main form layout twig file:
https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
An example:
{% block form_widget_simple %}
{% spaceless %}
{% set type = type|default('text') %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% endspaceless %}
{% endblock form_widget_simple %}
I wonder where varaibles like "type" or "value" come from?
The goal I'm trying to achieve is to set form row's label as a placeholder in the widget. How can I accomplish this?
Details how to override Template for Form field you will find here.
If you trying to change labels to placeholders all you need is to change way of rendering your forms. Remove form_widget(form) and switch to render every separate form field:
{# ... #}
<div class="form-group">
{{ form_errors(form.email) }}
{{ form_widget(form.email, {'attr': {'class': 'form-control', 'placeholder': 'E-mail address'|trans }}) }}
</div>
{# ... #}
This example generate input for email field and html/css classes for bootstrap.
And shows you how {{ type }} and {{ value }} are passed - by attr array.
Good Luck!
I need my symfony2/twig forms to adhere to a certain condition: All form rows must look similar to this:
{% block form_row %}
<div class="CONSTANT_CLASS class_based_on_field_type class_based_on_error">
{{ form_label(form) }}
{{ form_widget(form) }}
...
</div>
{% endblock form_row %}
Notice that I need to get the field type within the form_row block. Alas, the field type is only defined at the widget level.
I definitely need a way to let my form_row know what type of field it is dealing with.
So i suppose it would be best to somehow override the form_row twig function.
Where can the default twig functions be overridden? And how could this be done?
Remember, this is not about customizing a form. I need to know how to add to existing form variables for existing field types.
#nifr:
The key answer for you seems to be the {% set typeClass ... %}. But there is not a defined variable text anywhere for the template. Look at form_div_layout.html.twig at line 158ff, I think the type really gets set only at the form_widget level and is thus capsulated to be there. That means using the type at the form_row level will aways result in the given default (So it occurred to me while testing). If you can prove this wrong I will happily accept your answer.
How to override the form_row block in Twig adding attributes by field-type?
Though you say this is not about form customization it can be achieved with it ... Quick Introduction for others reading this now.
The default symfony twig form extensions can be found here.
The default twig form theme can be found at Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig.
General information on how to override forms can be found in the How to Customize Form Rendering chapter of the Book but i will sum this up shortly.
form_row Default
{% block form_row %}
{% spaceless %}
<div>
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
Overriding Form Level
Add this to the form template you want to customize:
{% form_theme form _self %}
If you want to put the {% block form_row %} into another bundle/template use this:
{% form_theme form 'AcmeDemoBundle:Form:fields.html.twig' %}
Now insert your custom form_row block after the form_theme declaration or put it into the specified template (in our case this would be AcmeDemoBundle:Form:fields.html.twig).
In my example we will add the class 'error' if there is an error in the form row and a another classname nameely the type name of the current field-type.
{% block form_row %}
{% spaceless %}
{# set class to 'error' if errors exist #}
{% set attr = attr|merge({'class': attr.class|default('') ~ (errors|length > 0 ? ' error' : '') }) %}
{% set typeClass = ' ' ~ type|default('text') %}
{#
you could also implement a logic matching input types with an array of their
desired classname-representations here.
#}
{% set attr = attr|merge({'class': attr.class|default('') ~ type) }) %}
<div class="{% for class in attr.class %}{{ class }}{% endfor %}{{ typeClass }}">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
if you want to apply your form_row block system-wide add your AcmeDemoBundle:Form:fields.html.twig to your twig.templating.form.resources !
# app/config/config.yml
framework:
templating:
form:
resources:
- 'AcmeDemoBundle:Form'
In the form_row block you can use :
{{ form.vars.block_prefixes[2] }}
form.vars.block_prefixes gives you an array with more information and it might change with versions. But from what I have seen so far it is always index 2