How to pass a variable to form_theme? - symfony

I want to theme my form so that the field's label show the current locale, something like
Name (en) :
So I would like to rewrite block generic_label like that :
{# form_theme.html.twig #}
{% block generic_label %}
{% spaceless %}
{% if required %}
{% set attr = attr|merge({'class': attr.class|default('') ~ ' required'}) %}
{% endif %}
<label{% for attrname,attrvalue in attr %} {{attrname}}="{{attrvalue}}"{% endfor %}>{{ label|trans }} (app.session.locale)</label>
{% endspaceless %}
{% endblock %}
and import it in my template :
{% form_theme options 'myBundle:Object:form_theme.html.twig' %}
but the app variable is not accessible in the form template.
How can I pass a variable to a form theme ?

In current version of twig (as for 2016) it is possible.
In your template use the following like this:
{{ form_row(form.content, {'testvar' : 'this is test variable'}) }}
Then, in your theme file, just use:
{{testvar}}
of course instead of form.content you will use the field name you need.
Cheers,
Chris

You need to create a form extension in order to get it done. Take a look at
http://toni.uebernickel.info/2011/11/25/how-to-extend-form-fields-in-symfony2.html
to learn how to create the extension.
To have access to session locale, make sure to inject the container. After that you'll be able to get any var value you want.

If the app variable is not available in the form theme it may be a bug. I suggest you create a ticket.
In the meantime you can use the current template as a theme. Something like...
{% form_theme form _self %}
{% block field_label %}
{% set attr = attr|merge({ 'for': id }) %}
{% if required %}
{% set attr = attr|merge({ 'class': attr.class|default('') ~ ' required' }) %}
{% endif %}
<label{% for attrname, attrvalue in attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ label|trans }} ({{ app.session.locale }})</label>
{% endblock %}
If you are using Symfony master (2.1) replace app.session.locale with app.request.locale.

Related

SonataAdminBundle templates - list and show fields template content duplication

My list and show fields contain the same content, but due the extending of base_list_field and base_show_field templates I have to create two separate templates.
list/location.html.twig:
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% block field %}
{{ object.getCity }}, {{ object.getCountry.getName }}
{% endblock %}
show/location.html.twig:
{% extends 'SonataAdminBundle:CRUD:base_show_field.html.twig' %}
{% block field %}
{{ object.getCity }}, {{ object.getCountry.getName }}
{% endblock %}
As you can see huge part of code is duplicated. Is there a way to check the page I am currently in in twig and then extend certain base template? In this case I would be able to use one file instead of two for same content.
In twig it's possible to extend/include a dynamic template :
{# do test here on which page you are or pass through controller #}
{% if true %}
{% set template = 'SonataAdminBundle:CRUD:base_show_field.html.twig' %}
{% else %}
{% set template = 'SonataAdminBundle:CRUD:base_list_field.html.twig' %
{% endif %}
{% extends template %}
{% block field %}
{{ object.getCity }}, {{ object.getCountry.getName }}
{% endblock %}
(edit) if you don't want the hardcoded if I would pass the template variable through the controller and change the twig template to something like
{% extends template|default('SonataAdminBundle:CRUD:base_show_field.html.twig') %}

how to change a label template using twig inheritance in symfony2

Using Symfony2.3.4 with Twig.
Say I´m trying to add, for example, a colon (:) and, if required, an asterisk (*) to every label of every field in a form generated by Symfony2's CRUD. For this I'm using a twig template to inherit Symfony2's main template form_div_layout.html.twig.
So far:
//config.yml
twig:
form:
resources:
- ::my_form_layout.html.twig
//my_form_layout.html.twig
{% block form_label %}
{% spaceless %}
{% if label is not sameas(false) %}
...
{% set asterisk = '<sup><i title="Campo obligatorio"
class="glyphicon-asterisk" style="color: red; font-size:
8px"></i></sup>' %}
<label {% for attrname, attrvalue in label_attr %}
{{ attrname }}="{{ attrvalue }}"{% endfor %}>
{{ label|trans({}, translation_domain) }}: {% if required %}
{{ asterisk|raw }} {% endif %}
</label>
...
{% endif %}
{% endspaceless %}
{% endblock form_label %}
Problem is this way when I render, for example, a choice-type field for selecting the sex of a person, with expanded and required set to TRUE, the colon (:) and the asterisk (*) appear next to the word Sex AND the words Male and Female as well.
How can I make the template differenciate between the parent and the two children, so the colon and the asterisk appear only after the word Sex.
Thanx
This is how I've done something similar. Modify the 'choice_widget_expanded' block a bit in your custom form layout:
// update this row: {{ form_label(child) }}
{{ form_label(child, child, {'exclude_additions': 'true'}) }}
And update your 'form_label' to check whether this value is defined:
{{ label|trans({}, translation_domain) }}{% if exclude_additions is not defined %}: {% if required %}
{{ asterisk|raw }} {% endif %} {% endif %}

Custom form field template with twig

I'd like to create a custom template in twig to render a form field.
Example:
{{ form_row(form.field) }}
This can be overriden by form theming
{% block form_row %}
... custom code
{% endblock form_row %}
What I would like to do is this:
{% block custom_row %}
... custom code
{% endblock custom_row %}
and use it like this:
{{ custom_row(form.field }}
however, this throws an exception that method custom_row is not found.
My understanding is that this can be done with Twig extension, but I don't know how to register a block to be a function.
Update
what I actually want:
I use twitter bootstrap and a bundle which overrides all the form themes. And it renders a div around a radio, so it can't be inlined. So I wanted to do something like this:
copy their template and get rid of the div:
{% block inline_radio_row %}
{% spaceless %}
{% set col_size = col_size|default(bootstrap_get_col_size()) %}
{% if attr.label_col is defined and attr.label_col is not empty %}
{% set label_col = attr.label_col %}
{% endif %}
{% if attr.widget_col is defined and attr.widget_col is not empty %}
{% set widget_col = attr.widget_col %}
{% endif %}
{% if attr.col_size is defined and attr.col_size is not empty %}
{% set col_size = attr.col_size %}
{% endif %}
{% 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 %}
{% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' radio-inline')|trim}) %}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>
{{ block('radio_widget') }}
{{ label|trans({}, translation_domain) }}
</label>
{% else %}
{{ block('radio_widget') }}
{% endif %}
{{ form_errors(form) }}
{% endspaceless %}
{% endblock inline_radio_row %}
and then
{{ inline_radio_row(form.field) }}
I ended up just overriding the whole theme, and added ifs around the div in question, a the class (radio-inline). But I'm still wondering if there's a way to make this work. Seems like it makes you work so hard for something so simple.
Update 2
I found the functionality:
class FormExtension extends \Twig_Extension
{
public function getFunctions()
{
return array(
'inline_radio_row' => new \Twig_Function_Node(
'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode',
array('is_safe' => array('html'))
),
);
}
}
This does exactly what I want, but it says it's deprecated. Anyone knows an updated version of how to use this?
Update 3
Similar functionality can be also achieved with http://twig.sensiolabs.org/doc/tags/include.html
You can use the twig functions for each part of a form row:
form_label(form.field)
form_widget(form.field)
form_errors(form.field)
For example:
<div class="form_row">
{{ form_label(form.field) }} {# the name of the field #}
{{ form_errors(form.field) }} {# the field #}
{{ form_widget(form.field) }} {# the errors associated to the field #}
</div>
You can use form theming.
Step by step:
1. Form Type Class
Check the name in your class
public function getName() {
return 'hrQuestionResponse';
}
2. Include a custom theme in your template
{% form_theme form 'InterlatedCamsBundle:Form:fields.html.twig' %}
3. Find the block
Can be quite difficult. For the bootstrap bundle, as you seem to have found it is in ./vendor/braincrafted/bootstrap-bundle/Braincrafted/Bundle/BootstrapBundle/Resources/views/Form/bootstrap.html.twig and you have found the block radio_row. I have been finding the block by putting output in the source template and overriding more blocks than I need. In 2.7 there is a theme 'rendering call graph'.
4. Override the block
Copy the block from the master template and call it replace the standard term with the name of in the FormType found in step 1.
Don't forget to also change the endblock name.
e.g.
{% block hrQuestionResponse_widget %}
hrQuestionResponse_row
{% spaceless %}
{% set class = '' %}
...
{% endspaceless %}
{% endblock hrQuestionResponse_widget %}
In your case because you can only call form_widget() you will need to override _widget. You could extract only the content that you need, or you can override the block chain to radio_row.

Twig and autoescaping

I'm learning Symfony2. Currently, I'm trying to render a form label in a twig template. The label includes an html tag, that is not rendered correctly in my twig file.
Here follows the piece of code where the form field is created:
$builder->add('zipcode', 'integer', array(
'label' => '<abbr title="Zone Improvement Plan">CAP</abbr> code',
));
In the twig file I render the field label as follows:
{{ form_label(form.zipcode) }}
I tried the raw, escape, e filters, but the results provided in my html page is always the string
<abbr title="Zone Improvement Plan">CAP</abbr> code
and not the corresponding HTML code!
Any suggestion?
Thanks in advance!
Later I found the solution.
The solution is to disable the autoescape within the label block provided by Symfony at path:
symfony / src / Symfony / Bridge / Twig / Resources / views / Form / form_div_layout.html.twig
So, in your twig file you have to put the following lines outside the form:
{% form_theme form _self %}
{% block generic_label %}
{% spaceless %}
{% if required %}
{% set attr = attr|merge({'class': attr.class|default('') ~ ' required'}) %}
{% endif %}
<label{% for attrname,attrvalue in attr %} {{attrname}}="{{attrvalue}}"{% endfor %}>{% autoescape false %}{{ label|trans }}{% endautoescape %}</label>
{% endspaceless %}
{% endblock %}
From JeanValjean himself :
{% autoescape false %}{{ form.zipcode.vars.label | trans }}{% endautoescape %}
And to generalize this behaviour to your whole app, you can override the form block for labels :
{% block generic_label %}
{% spaceless %}
{% if required %}
{% set attr = attr|merge({'class': attr.class|default('') ~ ' required'}) %}
{% endif %}
<label{% for attrname,attrvalue in attr %} {{attrname}}="{{attrvalue}}"{% endfor %}>
{% autoescape false %}{{ label|trans }}{% endautoescape %}
</label>
{% endspaceless %}
{% endblock %}
To disable the autoespace filter just to render a variable is not the best thing because when you read the code it's not really clear.
So, instead of :
{% autoescape false %}{{ label|trans }}{% endautoescape %}
You can use :
{{ label|trans|raw }}

Dynamically call a macro in Twig?

Is possible to dynamically call a macro in Twig? For example, here is a template and a macro named "group" which builds a button group using buttons array argument. There are also two other macros, save and delete, for building save and delete buttons.
{# Make a group of buttons #}
{% macro group(buttons) %}
{% spaceless %}
{% import "::macros.html.twig" as macros %}
{% set content = '' %}
{% for button in buttons %}
{% set content = content ~ button %}
{% endfor %}
{{ macros.el('div', content, { 'class' : 'btn-group' }) }}
{% endspaceless %}
{% endmacro %}
{# Make a save button #}
{% macro save(attributes, size, image) %}
{% spaceless %}
{{ _self.primary('save'|trans({}, 'buttons'), attributes, size, image) }}
{% endspaceless %}
{% endmacro %}
{# Make a delete button #}
{% macro delete(attributes, size, image) %}
{% spaceless %}
{{ _self.danger('delete'|trans({}, 'buttons'), attributes, size, image) }}
{% endspaceless %}
{% endmacro %}
This works fine passing an array of buttons:
{% import "::buttons.html.twig" as buttons %}
{% set items = [buttons.save, buttons.delete] %}
{{ buttons.group(items) }}
But i'd like to pass macro names to group macro:
{% import "::buttons.html.twig" as buttons %}
{{ buttons.group(['save', 'delete']) }}
and get save and delete macros called automatically. Is this possible and how?
why not just do
{% import "::buttons.html.twig" as buttons %}
{{ buttons.group([buttons.save, buttons.delete]) }}
For those looking for an example of how to implement dynamic macro calling using attribute, checkout https://gist.github.com/tentacode/9728963b9f3a714608f3

Resources