Twig: remove div around each checkbox fields of a form - twig

Edit: see the solution I've found at the end of my question ;-)
I'm looking since hours for a solution but I do not really find what I'like. I'm sure it is easy to do.
I'm building a form with Symfony 3.1. My form does not send anything in a database. Everything seems okay. But when rendering my twig template all my checkbox are surrounded by a <div> html tag.
I just would like twig renders me a form without that <div> tag.
edit: this is what Twig render me: this is ok for me but I would like to remove div tags
<form name="form" method="post">
<div id="form"><div><label for="form_1">1</label><input type="checkbox" id="form_1" name="form[1]" class="ballsCheckBox" value="1" /></div>
</form>
Here is my twig template:
{% extends "::base.html.twig" %}
{% block title %}SimulotoBundle:Lotto:lotto{% endblock %}
{% block body %}
<h1>Welcome to the Lotto:result page</h1>
{{form(form)}}
{% endblock %}
I build the form directly in a controller. See it:
public function LottoSimulationFormAction()
{
$lt = new LottoSimulation();
$data = [];
$formBuilder = $this->createFormBuilder($data);
/** building Lotto grid with checkboxes * */
for ($i = 1; $i <= $lt->getMaxNb(); $i++)
{
$formBuilder->add($i, CheckboxType::class, [
'label' => $i,
'required' => false,
'attr' => [
'class' => 'ballsCheckBox'
]
]);
}
/** adding submit button **/
$formBuilder->add('Envoyer', SubmitType::class, [
'attr' => [
'class' => 'save'
]
]);
$form = $formBuilder->getForm();
return $this->render("SimulotoBundle:Lotto:lotto.html.twig", [
"form" => $form->createview()
]);
}
}

Here is the soluton to solve this problem.
I need to Customize the form rendering as it is explained in the Synfony cookBook on this page http://symfony.com/doc/current/form/form_customization.html
Go to vendor\Symfony\Bridge\Twig\Ressources\views\Form\form_div_layout.html and modify the block:
{%- block form_row -%}
<div>
{{- form_label(form) -}}
{{- form_errors(form) -}}
{{- form_widget(form) -}}
</div>
{%- endblock form_row -%}
You can remove it but a better way is to copy/past that block to your teplate and overide it. Like this:
{% extends "::base.html.twig" %}
{% form_theme form _self %} //don't forget this line of code
{%- block form_row -%}
{{- form_label(form) -}}
{{- form_errors(form) -}}
{{- form_widget(form) -}}
{%- endblock form_row -%}

Related

Can't access global data or macro within macro

I'm pretty new to patternlab and am just migrating my php based project onto the node version.
I am having issues to access global data and block in a block file, which I didn't have previously.
I am using Pattern Lab Node v3.0 on Mac, with Node v13.9.0, using a Gulp Edition with Twig.
source/_data/data.json
"img": {
"landscape": {
"w_1024": {
"src": "../../images/1536x864_16x9.jpg",
"alt": "16x9 Image"
}
}
}
source/macros/ui.twig
{% macro icon( name ) %}
{% if name == "airplay" %}<img src="airplay.png"/>{% endif %}
{% endmacro %}
source/02-organisms/00-global/file-1.twig
{% import "#macros/blocks.twig" as blocks %}
{% import "#macros/ui.twig" as ui %}
<p class="icon">{{ ui.icon( "airplay" ) }}</p>
<p class="output">{{img.landscape.w_1024.src}}</p>
{{ blocks.media(item) }}
source/macros/blocks.twig
{% import "#macros/ui.twig" as ui %}
{% macro media( params ) %}
<p class="icon2">{{ ui.icon( "airplay" ) }}</p>
<p class="output2">{{img.landscape.w_1024.src}}</p>
{% endmacro %}
Expected Behavior
Generated html should look as follow :
<p class="icon"><img src="airplay.png"/></p>
<p class="output">../../images/1536x864_16x9.jpg</p>
<p class="icon2"><img src="airplay.png"/></p>
<p class="output2">../../images/1536x864_16x9.jpg</p>
Actual Behavior
Generated html looks as follow :
<p class="icon"><img src="airplay.png"/></p>
<p class="output">../../images/1536x864_16x9.jpg</p>
<p class="icon2"></p>
<p class="output2"></p>
Any help is welcome!
Macro's have their own variable scope. If you want to access any other defined variables you'd need to pass the special variable _context.
{% macro foo(bar, context) %}
{{ bar }}
{{ context['foo'] }}
{% endmacro %}
{% import _self as macros %}
{{ macros.foo(42, _context) }}
demo - demo with include

Symfony / a2lix_translations / customize

can someone help me.
How can I modify default template to bootstrap version?
Because input's doesn't have a class "form-control".
Here is defaul:
{% block a2lix_translations_widget %}
{{ form_errors(form) }}
<div class="a2lix_translations tabbable">
<ul class="a2lix_translationsLocales nav nav-tabs">
{% for translationsFields in form %}
{% set locale = translationsFields.vars.name %}
<li {% if app.request.locale == locale %}class="active"{% endif %}>
<a href="#" data-toggle="tab" data-target=".{{ translationsFields.vars.id }}_a2lix_translationsFields-{{ locale }}">
{{ locale|capitalize }}
{% if form.vars.default_locale == locale %}[Default]{% endif %}
{% if translationsFields.vars.required %}*{% endif %}
</a>
</li>
{% endfor %}
</ul>
<div class="a2lix_translationsFields tab-content">
{% for translationsFields in form %}
{% set locale = translationsFields.vars.name %}
<div class="{{ translationsFields.vars.id }}_a2lix_translationsFields-{{ locale }} tab-pane {% if app.request.locale == locale %}active{% endif %} {% if not form.vars.valid %}sonata-ba-field-error{% endif %}">
{{ form_errors(translationsFields) }}
{{ form_widget(translationsFields) }}
</div>
{% endfor %}
</div>
</div>
{% endblock %}
{% block a2lix_translationsForms_widget %}
{{ block('a2lix_translations_widget') }}
{% endblock %}
I have no idea what should I insert/delete/modify :(
Thanks
I have done a custom form template for a2lix_translations with bootstrap(full code is too long and not optimal to paste here) But to get the classes I need like form-control into the widgets I have done the following:
{%for field in translationsFields%} {# further break the transliationsfields into individual inputs #}
{%if field.vars.attr is not empty and field.vars.attr['class'] is defined and field.vars.attr['class']=="tinymce"%}
{{form_widget(field ,{'attr':{'class':' tinymcertl'}} )}}
{%else%}
{{form_widget(field,{'attr':{'style':'direction:rtl','class':class~' form-control'}} )}}
{%endif%}
{%endfor%}
The ugly code above is basically saying, if the widget already has a class, add the class form-group to it. If the widget does not have a class at all, set the class to be form-group. I have done that if statement to avoid null pointers since if I try to reference the form class and there isn't one, the code will crash. And if I just do set class to form-group, it will erase the previous classes.
I hope this helps. My full code might not be helpful to you because the languages I was working with involved left to right language and right to left languages, so a lot of conditions had to be implemented to orient my page in the right direction, which is messy and you might not need...
PS: this was done on symfony 2.7 or so. Did not test on symfony 3.
in my case sf 3.2 i just did this change in my config.yml and all forms are bootstraped :
# app/config/config.yml
twig:
//....
form_themes:
- 'bootstrap_3_layout.html.twig'

variables {% set %} in a skeleton file's 'block' don't get recognized , and can't be used when extnding the file?

I have a symfony3/twig skeleton template
page1/skeleton.twig
{# set default values #}
{% block content %}
{% set test = {
sec1: {
title: "null",
content: 'null'
},
}
%}
{% endblock %}
<ul>
19 {% for sec in test[0:] %}
<li>
<p>{{ sec.title }}</p>
<div>
<p>{{ sec.content }}</p>
</div>
</li>
{% endfor %}
</ul>
I then create a layout template that extends the skeleton with 'real' data
page1/layout.html.twig
{% extends 'page1/skeleton.html.twig' %}
{% block content %}
{% set test = {
sec1: {
title: "title1",
content: 'content2'
},
sec2: {
title: "title2",
content: 'content2'
}
%}
{% endblock %}
But when I generate/publish the page, Symfony fires an error
Variable "test" does not exist in :page1:skeleton.html.twig at line 19
500 Internal Server Error - Twig_Error_Runtime
complaining about the skeleton itself.
That 'test' array is defined in the skeleton. Afaict from reading the docs on 'block', 'extends' & 'set', and can't figure out what exactly the problem is.
What do I need to change to eliminate this error?
blocks in twig have their own variable scope.Variables created inside a block can't be accessed outside of it.
Imo you should only test if the variable exist and otherwise create the default value :
skeleton.twig
{% if not test is defined %}
{%
set test = {
sec1: {
title: "null",
content: 'null'
},
}
%}
{% endif %}
<ul>
{% for sec in test[0:] %}
<li>
<p{{ sec.title }}</p>
<div>
<p>{{ sec.content }}</p>
</div>
</li>
{% endfor %}
</ul>
controller.php
<?php
echo $twig->render('page/page.twig', array(
'foo' => [
'title' => 'title1',
'content' => content1',
],
);
Change this in the file page1/skeleton.twig:
{% for sec in test %}
Then it will work.
I tried it. Make sure you understand why!

Silex - Symfony Froms - How to wrap each radio button and its label in to separate div for each radio in choice list?

I am using Silex micro faramework with Symfony forms. In twig templet I generate this field using :
...
{{ form_widget(form.transport_selection) }}
...
How can I overwrite this symfony twig form templet to generate wrap for each set (input field and label) in Silex.
This is my twig registration:
use Silex\Provider\FormServiceProvider;
use Symfony\Component\Translation\Loader\YamlFileLoader;
use Symfony\Component\Validator\Constraints as Assert;
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => [
__DIR__.'/App/View',
]
));
How to wrap radio button and label with div for each radio choice ?
output:
<div id="form_transport_selection">
<input type="radio" id="form_transport_selection_0" name="form[transport_selection]" required="required" value="country" checked="checked">
<label for="form_transport_selection_0" class="required">country</label>
<input type="radio" id="form_transport_selection_1" name="form[transport_selection]" required="required" value="abroad">
<label for="form_transport_selection_1" class="required">abroad</label>
</div>
Expected output:
<div id="form_transport_selection">
<div class="radio1">
<input type="radio" id="form_transport_selection_0" name="form[transport_selection]" required="required" value="country" checked="checked">
<label for="form_transport_selection_0" class="required">country</label>
</div>
<div class="radio2">
<input type="radio" id="form_transport_selection_1" name="form[transport_selection]" required="required" value="abroad">
<label for="form_transport_selection_1" class="required">abroad</label>
</div>
</div>
Thanks
You can make new template for this form and redefine radio buttons view
my.form.twig
{% extends "form_div_layout.html.twig" %}
{%- block choice_widget_expanded -%}
<div {{ block('widget_container_attributes') }}>
{% set i=1 %}
{%- for child in form %}
<div class="radio-{{i}}">
{{- form_widget(child) -}}
{{- form_label(child, null, {translation_domain: choice_translation_domain}) -}}
</div>
{% set i=i+1 %}
{% endfor -%}
</div>
{%- endblock choice_widget_expanded -%}
and use it in controller template, where form is shown
{% form_theme form 'my.form.twig' %}
....
{{ form_widget(form.transport_selection) }}
Expanding on MaxP's answer. Make sure to only modify this for radio buttons, you don't want this used in checkboxes! I've added a different class for checkboxes. Note the if/else to do this.
{% extends "form_div_layout.html.twig" %}
{% block choice_widget_expanded %}
<div {{ block('widget_container_attributes') }}>
{% set i=1 %}
{% for child in form %}
{% if form.vars.multiple == false %}
<div class="radio-{{i}}">
{% elseif form.vars.multiple == true %}
<div class="checkbox-{{i}}">
{% endif %}
{{ form_widget(child) }}
{{ form_label(child, null, {translation_domain: choice_translation_domain}) }}
</div>
{% set i=i+1 %}
{% endfor %}
</div>
{% endblock choice_widget_expanded %}
And use it in controller template, where form is shown
{% form_theme form 'my.form.twig' %}
....
{{ form_widget(form.transport_selection) }}
When using Symfony, I recommend overriding all Twig fields in app/Resources/views/Form/fields.html.twig
And then on config.yml you just need to make this available by adding it to twig's config
twig:
form_themes:
- 'Form/fields.html.twig'

Can I put html inside a Symfony form button with Twig?

I'm trying to put html inside a form button with twig like:
{{ form_widget(form.jiraStatus, {
'label': '<i class="fa fa-bug"></i>Bug',
'attr':{'class': 'btn btn-large btn-default btn-block' }
}) }}
But doing this, the rendeded button shows like this:
<button type="submit" name="SolveTask[taskTypesFormObj][bugStatus]"
class="btn btn-large btn-default btn-block">
<i class="fa fa-bug"></i>Bug
</button>
As you can see, the html inside the button is encoded. I tried to use the raw filter, but the effect is the same. There is a way to do this?
Thanks!
Yes, but you'll have to customise your form theme.
Note: This answer has been edited to be compatible with Symfony 2.8 3.x and 4.x. For older versions, please see the edit history.
A nice way to support icons in buttons is using form extensions. First create a form extension class that defines a new property icon that you can use in your forms:
<?php
namespace Foo\BarBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ButtonTypeIconExtension extends AbstractTypeExtension
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->setAttribute('icon', $options['icon']);
}
/**
* #param FormView $view
* #param FormInterface $form
* #param array $options
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['icon'] = $options['icon'];
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['icon' => null]);
$resolver->setDefined(['icon']);
}
/**
* Returns the name of the type being extended.
*
* #return string The name of the type being extended
*/
public function getExtendedType()
{
return ButtonType::class; // Extend the button field type
}
}
Register this extension in your services.yml (or xml file). The alias must correspond with the string returned by the above getExtendedType() method.
# Form extension for adding icons
foobar.form_extension.icon:
class: Foo\BarBundle\Form\Extension\ButtonTypeIconExtension
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\ButtonType }
Next, override your form_div_layout.html.twig. (See link above) You can now use icon as a variable in these themes. For buttons we override the button_widget block:
{% block button_widget -%}
{% set attr = attr|merge({class: (attr.class|default('') ~ ' btn')|trim}) %}
{% 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 -%}
{% if icon|default %}
{% set iconHtml = '<i class="fa ' ~ icon ~ '"></i> ' %}
{% else %}
{% set iconHtml = '' %}
{% endif %}
<button type="{{ type|default('button') }}" {{ block('button_attributes') }}>{{ iconHtml|raw }}{{ label|trans({}, translation_domain) }}</button>
{%- endblock button_widget %}
Finally, you can use the icon option in your template:
{{ form_widget(form.jiraStatus, {
'icon': 'fa-bug',
'label': 'Bug',
'attr':{'class': 'btn btn-large btn-default btn-block' }
}) }}
Or in your form classes:
$builder
->add('jiraStatus', SubmitType::class, [
'label' => 'Bug',
'icon' => 'fa-bug',
'attr' => [
'class' => 'btn btn-large btn-default btn-block',
],
]
);
Note: Is is generally better to add the icon in the template since icons are a matter of presentation, and your form classes should really be about buisness logic.
Make it even more generic:
By returning the FQCN of ButtonType in getExtendedType() we tell Symfony that we are extending all possible form elements that inherit from ButtonType such as SubmitType. Unfortunately there is no type we can use to target all possible form elements but we can add an extra extension that targets FormType. All form fields like input boxes and select elements inherit from this type. So if you want it to work with both form fields and buttons, I suggest the following:
Create an abstract class abstract class AbstractIconExtension extends AbstractTypeExtension with exactly the same content as above but leave out the getExtendedType method. Then create two classes that extend from this class (e.g. FieldTypeIconExtension and ButtonTypeIconExtension) which only contain the getExtendedType method. One returning the FQCN of FormType and the other returning the FQCN of ButtonType:
Foo/BarBundle/Form/Extension/ButtonTypeIconExtension.php:
<?php
namespace Foo\BarBundle\Form\Extension;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
class ButtonTypeIconExtension extends AbstractIconExtension
{
/**
* Returns the name of the type being extended.
*
* #return string The name of the type being extended
*/
public function getExtendedType()
{
return ButtonType::class; // extend all buttons
}
}
Foo/BarBundle/Form/Extension/FieldTypeIconExtension.php:
<?php
namespace Foo\BarBundle\Form\Extension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
class FieldTypeIconExtension extends AbstractIconExtension
{
/**
* Returns the name of the type being extended.
*
* #return string The name of the type being extended
*/
public function getExtendedType()
{
return FormType::class; // extend all field types
}
}
Register these two classes in you services using the corresponding alias:
# Form extensions for adding icons to form elements
foobar.form_extension.button_icon:
class: Foo\BarBundle\Form\Extension\ButtonTypeIconExtension
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\ButtonType }
foobar.form_extension.form_icon:
class: Foo\BarBundle\Form\Extension\FieldTypeIconExtension
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType }
Now you can use the icon variable in other places in your form themes as well. For instance, to add icons to labels you can override the form_label block:
{% block form_label -%}
{% 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 -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
{% if icon|default %}
{% set iconHtml = '<i class="fa ' ~ icon ~ '"></i> ' %}
{% else %}
{% set iconHtml = '' %}
{% endif %}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>{{ iconHtml|raw }}{{ label|trans({}, translation_domain) }}</label>
{%- endif %}
{%- endblock form_label %}
And then add an icon to the label of that field in your form class:
$builder
->add('mytextfield', TextType::class, [
'label' => 'My fancy text field',
'icon' => 'fa-thumbs-o-up'
]
);
If you're looking for a simpler solution, just insert this into your form theme:
{%- block button_widget -%}
{% set attr = attr|merge({class: (attr.class|default('btn-default') ~ ' btn')|trim}) %}
{%- 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 -%}
<button type="{{ type|default('button') }}" {{ block('button_attributes') }}>{{ label|trans({}, translation_domain)|raw }}</button>
{%- endblock button_widget -%}
You can then go ahead and insert HTML in your button label:
{{ form_widget(searchForm.search, {'label': '<span class="glyphicon glyphicon-search" aria-hidden="true"></span>'}) }}
this is how I solved and tested with Symfony 4.
In twig template:
{{form_start(form)}}
{{form_widget(form)}}
<div class="class row">
<div class="class col-sm-offset col-sm-10">
<button name='create' type='submit' value='create' class='btn btn-primary'>Save</button>
<button name='cancel' type='submit' value='cancel' class='btn btn-cancel' formnovalidate='formnovalidate'>Cancel</button>
</div>
</div>
{{form_end(form)}}
In my PHP controller form I did not add any button but just input fields.
$form = $this->createFormBuilder($article)
->add('title',TextType::class, array(
'data' => $article->getTitle(),
'attr' => array('class' => 'form-control')))
->add('body', TextareaType::class, array(
'data' => $article->getBody(),
'required' => false,
'attr' => array('class' => 'form-control')))
->getForm();
What I did to check the form submission is:
if($form->isSubmitted() ){
if($request->request->get('create') && $form->isValid()){
$article = $form->getData();
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($article);
$entityManager->flush();
} //l'alternativa può solo essere il cancel
return $this->redirectToRoute('article_list');
}
Hope this can help. Good to say that even the problem of buttons alignment is solved because div is not added for every button as form add method does.
On Symfony 5.1, you can add html content into button label like this:
{{ form_widget(form.submit, { 'label': '<i class="fas fa-calculator"></i> Calculate prices', 'label_html' : true })}}
You need to pass the option "label_html" : true
You can view the original Symfony/Twig code from form_div_layout.html.twig file to understand it:
{%- block button_widget -%}
{%- if label is empty -%}
{%- if label_format is not empty -%}
{% set label = label_format|replace({
'%name%': name,
'%id%': id,
}) %}
{%- elseif label is not same as(false) -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<button type="{{ type|default('button') }}" {{ block('button_attributes') }}>
{%- if translation_domain is same as(false) -%}
{%- if label_html is same as(false) -%}
{{- label -}}
{%- else -%}
{{- label|raw -}}
{%- endif -%}
{%- else -%}
{%- if label_html is same as(false) -%}
{{- label|trans(label_translation_parameters, translation_domain) -}}
{%- else -%}
{{- label|trans(label_translation_parameters, translation_domain)|raw -}}
{%- endif -%}
{%- endif -%}
</button>
{%- endblock button_widget -%}
An even simpler solution might be to leave the buttons out of the form type and set name and value attributes. Then retrieve them as you would normal post parameters in the controller.
In your template:
{{ form_start(form) }}
<button name="clicked" value="saveDraft" class="btn btn-warning">
<i class="fa fa-square-o"></i> Save as Draft
</button>
<button name="clicked" value="saveComplete" class="btn btn-warning">
<i class="fa fa-check-square-o"></i> Save as Complete
</button>
Then in your controller
if ($form->isSubmitted() && $form->isValid()) {
$clicked = $request->request->get('clicked');
}

Resources