Link in form label using route - symfony

In my registration form i have a checkbox "I accept the terms", and want to link the word "terms" to my terms page.
Is there a way to add a link to a form label, using a route? (preferably without injecting the container in the form)

As the solution above somehow didn't work for me I solved it using the solution suggested here: https://gist.github.com/marijn/4137467
OK, so here i how I did it:
{% set terms_link %}<a title="{% trans %}Read the General Terms and Conditions{% endtrans %}" href="{{ path('get_general_terms_and_conditions') }}">{% trans %}General Terms and Conditions{% endtrans %}</a>{% endset %}
{% set general_terms_and_conditions %}{{ 'I have read and accept the %general_terms_and_conditions%.'|trans({ '%general_terms_and_conditions%': terms_link })|raw }}{% endset %}
<div>
{{ form_errors(form.acceptGeneralTermsAndConditions) }}
{{ form_widget(form.acceptGeneralTermsAndConditions) }}
<label for="{{ form.acceptGeneralTermsAndConditions.vars.id }}">{{ general_terms_and_conditions|raw }}</label>
</div>

The best way is to overwrite the twig block used to render that specific label.
First, check the form fragment naming section of the docs. Then create a new block in your form template with the the appropriate name. Don't forget to tell twig to use it:
{% form_theme form _self %}
For the next step check the default form_label block.
You'll probably only need a portion of it, something like this (I'm leaving the default block name here):
{% block form_label %}
{% spaceless %}
<label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>
{{ label|trans({}, translation_domain) }}
</label>
{% endspaceless %}
{% endblock %}

As an option, you can do so:
->add('approve', CheckboxType::class, [
'label' => 'Text part without link',
'help' => 'And download it',
'help_html' => true,
])

In Symfony 5.1 there are new form improvements.
HTML contents are allowed in form labels!
HTML contents are escaped by default in form labels for security reasons. The new label_html boolean option allows a form field to include HTML contents in their labels, which is useful to display icons inside buttons, links and some formatting in checkbox/radiobutton labels, etc.
// src/Form/Type/TaskType.php
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// ...
->add('save', SubmitType::class, [
'label' => ' Save',
'label_html' => true,
])
;
}
}
In your case you can set form label directly from template and pass the route there.
{{ form_widget(form.acceptTermsAndConditions, {
label: '' ~ "I accept ..."|trans ~ '',
label_html: true
})
}}

My solution was another:
form:
$builder
->add(
'agree_to_rules',
'checkbox',
[
'required' => true,
'label' => 'i_agree_to'
]
);
And html:
<span style="display:inline-block">
{{ form_widget(form.agree_to_rules) }}
</span>
<span style="display:inline-block">
rules
</span>
And looks the same :)

A very simple way to do it would be
{{ form_widget(form.terms, { 'label': 'I accept the terms and conditions' }) }}
You can also do this if you want to use translation
In your translation file for example messages.en.yml add
terms:
url: 'I accept the terms and conditions'
And in your view add
{{ form_widget(form.terms, { 'label': 'terms.url'|trans({'%url%': path('route_to_terms')}) }) }}

Related

Nested forms in symfony empty after form submit

I have a question related to nested forms in symfony.
I have this form
{{ form_start(form, {'attr': {'class': 'needs-validation'}} ) }}
....
<div id="slot-fields-list" data-prototype="
{% filter escape %}
{% include 'slot/form/prototype.html.twig' with {'form': form.slot.vars.prototype} %}
{% endfilter %}"
data-widget-tags="{{ '<span></span>'|e }}">
</div>
{{ form_end(form) }}
.......................................
->add('slot', CollectionType::class, [
'entry_type' => SlotType::class,
'entry_options' => ['label' => false],
'label' => false,
'allow_add' => true,
'allow_delete' => true,
'error_bubbling' => false
])
Work fine, I have data in data-prototype after render template. When I submit the form and if the form has erreurs this nested form is added after submit button and data-prototype is empty. Any ideas ? Thx in advance
The documentation for CollectionType is actually quite good, but perhaps some particular details need reiteration.
First the documentation tells you, how to render the collection items (your slots probably):
<ul class="tags">
{# iterate over each existing tag and render its only field: name #}
{% for tag in form.tags %}
<li>{{ form_row(tag.name) }}</li>
{% endfor %}
</ul>
This is what's missing in your case. Just to avoid confusion, their <ul class="tags"> is your <div id="slot-fields-list" ...>. You have to go through the form.slot with
{% for slot in form.slot %}
{{ form_row(slot) }} <!--- or render your sub-form properly -->
{% endfor %}
now. Symfony is very reluctant to omit form elements. So when you place a {{ form_end(form) }} it will render the rest of the form elements, which have not been rendered before, to your form, before closing the <form>. (the same behaviour can be achieved by {{ form_rest(form) }}, if you ever need it)
That is the reason, why your subforms will appear after your submit button and completely out of place - because they weren't rendered anywhere before.
However, this does not yet explain the missing data-prototype, but perhaps there is a simple explanation as well ...
you possibly already know about the form themes, where you could add your own slot_widget, slot_row, slot_label and have symfony do all that weird include-stuff for you.

Label is not displaying in Symfony form

I am facing a issue to display label.
Following is the code to generate form element.
$builder->add(
'hearAboutUs', 'choice', [
'choices' => ['Online Search'=>'Online Search',
'Email'=> 'Email',
'My Company' => 'My Company',
'Colleague or Friend'=>'Colleague or Friend',
'Existing Client' =>'Existing Client',
'Direct Mail' => 'Direct Mail',
'Other'=> 'Other',],
'label' => 'How did you hear about us?',
'required' => true,
'expanded'=> true,
'multiple' => false,
]
);
I am getting following output.
As like "Notes" label "How did you hear about us" label is not displaying.
You can try to override your form theme about choice widget.
You can do it like this :
1- Create a file named fields.html.twig in app/Resources/views/form/
2- In this file you have to extends default twig layout and override checkbox_widget adding <label> tag :
{% extends 'form_div_layout.html.twig' %}
{% block checkbox_widget %}
{% spaceless %}
<label>
<input type="checkbox" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
</label>
{% endspaceless %}
{% endblock checkbox_widget %}
3- Finally, tell Symfony to use it in your view like this :
{% form_theme form 'form/fields.html.twig' %}
My View is looks like
My Updated Entity is
I have handled this using JQuery.
On Form Load I am appending Label and for required I have handled this on the controller.
But this is not proper solution, but by this way my problem solved.

Symfony: trying to customize a collection form prototype

I have this form:
class BillType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user')
->add('numberPlate')
->add('servicesPerformed', CollectionType::class, array(
'label' => false,
'entry_type' => ServicePerformedType::class,
'allow_add' => true,
))
->add('Save', SubmitType::class)
;
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'DefaultBundle\Entity\Bill'
));
}
being ServicePerformedType class this:
class ServicePerformedType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('description', TextareaType::class, array('label' => false))
->add('price', TextType::class, array('label' => false))
->add('quantity', TextType::class, array('label' => false));
}
}
And this template to render the form:
{{ form(form) }}
Add service
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script type="text/javascript">
var index = 0;
$('a').on('click', function() {
var prototype = $('#bill_servicesPerformed').data('prototype');
prototype = prototype.replace(/_name_/g, index.toString());
$('#bill_servicesPerformed').html(prototype);
index++;
})
</script>
As it is said in the docs, to get a custom prototype I should add the lines below at the top of my template:
{% form_theme form _self %}
{% block _servicesPerformed_entry_widget %}
I WILL WRITE MY CUSTOM PROTOTYPE HERE
{% endblock %}
But when I press Add service I dont get the text I WILL WRITE MY CUSTOME PROTOTYPE HERE, but the description, fields and quantity related to the ServicePerformedType class as before..
NOTE: maybe there are other ways to custom a form prototype, but I'm interested on this, so will be very thankful to someone who give a solution related to this way to custom form prototypes, thanks.
I must warn you that customizing the prototype could be a bit tricky. If you change your FormType fields you'll need to go through the template and make the same changes as well or your form will fail to render.
What I like to do is to create a custom template for that specific field and then customize it appropriately. So, looking at your code I would do something like this:
Create a page template - the one you'll use to render the entire page, including the form.
{# MyBundle/Resources/views/myPage.html.twig #}
{% extends "::base.html.twig" %}
{# This will tell the form_theme to apply the
`MyBundle:form/fields/servicePerformed.html.twig` template to your "form" #}
{% form_theme form with [
'MyBundle:form/fields/servicePerformed.html.twig'
] %}
{% block body %}
<div>
{{ form_start(form) }}
{{ form_rest(form) }}
{{ form_end(form) }}
</div>
{% endblock %}
Now you'll need to create the template MyBundle/Resources/views/form/fields/servicePerformed.html.twig. It will be used to customize only the servicePerformed field. The template should look something like this
{% macro service_template(fields) %}
<tr>
<td>I WILL WRITE MY CUSTOM PROTOTYPE HERE</td>
</tr>
{% endmacro %}
{#
The block name must match your field name!
You can find it by going into the debug toolbar -> Forms ->
click on your form field and then search for "unique_block_prefix".
Copy that and add "_widget" at the end.
#}
{% block _service_performed_widget %}
<table data-prototype="{{ _self.service_template(form.vars.prototype)|e }}">
{% for fields in form.children %}
{{ _self.service_template(fields) }}
{% endfor %}
</table>
{% endblock %}
I want to note that in the field template I'm passing the original prototype _self.service_template(form.vars.prototype). By doing this, you can use the original fields and render them in your customized prototype. For example this code:
{% macro service_template(fields) %}
<tr>
<td>{{ form_widget(fields.description) }}</td>
</tr>
{% endmacro %}
Will result in something like the following rendered prorotype:
<tr>
<td>
<input type="text" id="service_performed___name___description" name="service[__name__][description]"/>
</td>
</tr>
You can then manipulate it using your javascript.
I hope this helps you.
Actually form theme blocks that start with an underscore _ relate to a field with a specific name.
What I mean is that, if your master form BillType is called my_form, you will need to do this:
{% form_theme form _self %}
{% block _my_form_servicesPerformed_entry_widget %}
I WILL WRITE MY CUSTOM PROTOTYPE HERE
{% endblock %}
The problem with this approach is that it concerns a specific iteration of BillType. If you use this form type elsewhere and provide it with a different name my_form_2, you would have to add an identical block named _my_form_2_servicesPerformed_entry_widget.
You can use macro, have a look at below example it's working fine for me even in Symfony3. Using this example you will be able to format your collection prototype as well.
View
{% macro widget_prototype(widget, remove_text) %}
{% if widget.vars.prototype %}
{% set form = widget.vars.prototype %}
{% set name = widget.vars.name %}
{% else %}
{% set form = widget %}
{% set name = widget.vars.full_name %}
{% endif %}
<div data-content="{{ name }}" class="panel panel-default">
<div class="section row">
<div class="col-md-12">
<label class="field-label">Skill <span class="text-danger">*</span></label>
<label class="field select">
{{ form_widget(form.skill) }}
<i class="arrow double"></i>
</label>
</div>
</div>
<div data-content="{{ name }}">
<a class="btn-remove" data-related="{{ name }}">{{ remove_text }}</a>
{{ form_widget(form) }}
</div>
</div>
{% endmacro %}
Your template to render the form has some problems. The first is this line:
prototype = prototype.replace(/_name_/g, index.toString());
The regex should be __name__.
Next, you are retrieving the prototype, but then immediately overwriting it and replacing the HTML of the prototype. There is nothing there I can see that actually appends the new form to your existing form anywhere. Plus since you just have a string of text, that replace isn't going to find any text __name__ to replace.
You should post the full extent of your Twig/Javascript so we can actually see the #bill_servicesPerformed as well as everything else you are trying to do. Before you write a custom prototype you should get the form working with the standard prototype just to make sure you don't have any bugs there first.
As an example, this is the way I keep going it. I do not know if there are some reasons not to, so be careful.
Form to include prototype:
<div class="modal-body" id="contactMehtods" data-prototype="
{% filter escape %}
{{ include(':organization/partials:prototype_contact_method.html.twig', { 'contact_method': contact_form.contactMethods.vars.prototype }) }}
{% endfilter %}">
<div class="form-group">
{{ form_label(contact_form.name, null, { 'label_attr': { 'class': 'control-label' }}) }}
{{ form_widget(contact_form.name, {'attr': {'class': 'form-control'}}) }}
</div>
</div>
Prototype template:
<div class="form-group">
{{ form_label(contact_method.name, null, { 'label_attr': { 'class': 'col-sm-3 control-label' }}) }}
<div class="col-sm-9">
{{ form_widget(contact_method.name, {'attr': {'class': 'form-control'}}) }}
</div>
</div>
<div class="form-group">
{{ form_label(contact_method.value, null, { 'label_attr': { 'class': 'col-sm-3 control-label' }}) }}
<div class="col-sm-9">
{{ form_widget(contact_method.value, {'attr': {'class': 'form-control'}}) }}
</div>
</div>
But a note to consider, the javascript needs to accommodate for these changes I guess.

Printing all errors in one go within twig

I want to print all errors in one go for all the fields however {{ form_errors(form) }} doesn't print anything so because of that I have to use if not form.vars.valid statement to print errors however all the individual error messages are being wrapped within <ul><li>Message</li></ul> which is annoying. I know that 'error_bubbling' => true solves the issue but creates another issue which is making field borders red.
How can I solve this issue? I simply want to print error without tags.
Note: I can use {{ form_errors(form.name)|striptags }} but it adds overheads cos my form is massive.
FORM TYPE
class BrandsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setAction($options['action'])
->setMethod('POST')
->add('name', 'text', array('label' => 'Name'))
->add('button', 'submit', array('label' => 'Submit'))
;
}
}
TWIG
{% extends '::base.html.twig' %}
{% block body %}
{{ form_start(form, {attr: {novalidate:'novalidate'}}) }}
{% if not form.vars.valid %}
<div class="global_form_errors">
{{ form_errors(form.name) }}
{{ form_errors(form.origin) }}
</div><br />
{% endif %}
<div>
{{ form_label(form.name) }}
{% if form.name.vars.errors|length != '' %}
{{ form_widget(form.name, { attr: {'class': 'field_red_border'} }) }}
{% else %}
{{ form_widget(form.name) }}
{% endif %}
</div>
<div>
{{ form_widget(form.button) }}
</div>
{{ form_end(form) }}
{% endblock %}
Customize the field template
You can apply a form template to your form, check this documentation at Form Theming chapter.
If you prefer theming the field directly in your template, check the gray box in this documentation at the Global Form Theming chapter.
Check this documentation for more form customization.
Pass the errors to the template
When you submit the form, your code will call this method $form->handleRequest($request); which will check if the user input properly the data.
Based on the documentation of the Form class you can get a list of errors by calling $form->getErrors(). The variable $form is an instance of the Form class and not the FormView class. You may want to enable error bubbling.
Now that you know how to get the errors you can pass them as a variable to your template.

Adding "help" messages to fields

I'm trying to add some help messages after each field in form in symfony2.
I have read about one solution in official docs : http://symfony.com/doc/current/cookbook/form/form_customization.html#adding-help-messages
But this solution makes little sense, because we've need to create all form manually.
For example, it easy to define label: $formBuilder->add('myfieldname', 'text', array('label'=>'some my field label')); But how to pass help messages? (In other words, some custom variables)
A another method without another extension :
In your form builder class:
$builder->add('yourField',null, array('attr'=>array('help'=>'text help')))
In your form template rewrite:
{% block form_row %}
{% spaceless %}
{{ form_label(form) }}
{{ form_widget(form) }}
{% for attrname, attrvalue in attr %}
{% if attrname == 'help' %}
<span class="help-block">{{ attrvalue }}</span>
{% endif %}
{% endfor %}
{{ form_errors(form) }}
{% endspaceless %}
{% endblock form_row %}
$formBuilder->add('myFieldName', 'text', array('help' => 'My Help Message')); But it think you also need to add an extension that add this as a default option for all forms :
https://github.com/simplethings/SimpleThingsFormExtraBundle#helpextension
This makes you able to edit attributes directly from you FormTypes.
Since symfony 4.1 you can do :
$builder->add('email', null, [
'help' => 'Make sure to add a valid email',
]);
https://symfony.com/blog/new-in-symfony-4-1-form-field-help
You can use the solution in the official docs as you described.
But, the work is not complete yet. You have to create a Form Type Extention, based on this article: http://symfony.com/doc/current/cookbook/form/create_form_type_extension.html
After complete the Form Type Extention creation you can add Help Messages like this:
$form = $this->createFormBuilder()
->add('name', 'text', array(
'help' => 'this is a help message to user',
))
I think this is a native better solution.
Also, i recommend read this great article that shows you how to enable and set the help option in symfony2 forms:
http://toni.uebernickel.info/2012/11/03/how-to-extend-form-fields-in-symfony2.1.html
A little off topic but still useful if you're planning to use Bootstrap for your project then you can take advantage of some form helpers provided by the Mopa Bootstrap Bundle.
Demo: http://bootstrap.mohrenweiserpartner.de/mopa/bootstrap/forms/help_texts
GitHub: https://github.com/phiamo/MopaBootstrapBundle
Example:
<?php
$form = $this->get('form.factory')
->createNamedBuilder('form_name')
->setMethod('POST')
->add('testSelect', 'choice', [
'choices' => ['val1' => 'Value 1', 'val2' => 'Value 2'],
'required' => true,
'help_block' => 'Here some help text!!!'
])
->add('Save', 'submit')
->getForm();
return $form->createView();

Resources