Symfony2 Form Builder - Remove label, make it placeholder - php

I am playing with Symfony's form builder, and I can't find a way to not display a label. Further, I am interested in actually setting a placeholder for each input box. Is this possible? I have researched a bit and found nothing.
My form:
<form action="{{ path('searchPeople') }}" method="post" class="form-inline">
{{ form_errors(form) }}
{{ form_row(form.first_name) }}
{{ form_row(form.last_name) }}
{{ form_rest(form) }}
<br />
<button type="submit" class="btn btn-primary" /><i class="icon-search"></i>Search</button>
</form>

I know it's already answered, but might help somebody who is looking for a different solution for placeholders, if you don't want to change anything in your twig template:
$builder->add(
'name',
'text',
array(
'attr' => array(
'placeholder' => 'Your name',
),
'label' => false,
)
);

If you're outputting the field with form_rest you'll have to set the label for the the field to false in the form builder with something like
$builder->add('first_name', 'text', array(
'label' => false,
));
If you output the fields individually, you can omit the form_label for that field in the twig template, or set it to an empty string.
{{ form_label(form.first_name, '') }}

Convert label to placeholder
{% use 'form_div_layout.html.twig' with widget_attributes as base_widget_attributes %}
{% block widget_attributes %}
{% set attr = {'placeholder': label|trans({}, translation_domain)} %}
{{- block('base_widget_attributes') -}}
{% endblock widget_attributes %}

I did this recently! :) You'll want to create a new fields template, for form_row and one for form_widget. Then remove the form_label part, and add your placeholder.
http://symfony.com/doc/current/cookbook/form/form_customization.html
You can do it per field, or set it for all of them.
Or you can also skip the removing the form_label from the form_row template, and just do form_widget() where you're currently calling form_row()

for other that come across this label-question:
you could use form theme to override the form_row tag for every form you want. However I recommend to just set it invisible for page reader optimization. my example with bootstrap:
{% block form_row %}
{% spaceless %}
{{ form_label(form, null, {'label_attr': {'class': 'sr-only'}}) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
{% endspaceless %}
{% endblock form_row %}
don't forget to include your formtheme in config.yml and template.

For those NOT using form_row, you can always add the placeholder as an attribute directly when adding the input to the builder. Like so:
$task = new Task();
$form = $this->createFormBuilder($task)
->add('first_name', 'text', array(
'required' => true,
'trim' => true,
'attr' => array('placeholder' => 'Lorem Ipsum')
)->getForm();

Symfony 2.8 & above
Remove form_label
{% block form_row %}
<div>
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endblock form_row %}
Add placeholder attribute
{% block form_widget_simple %}
{% set type = type|default('text') %}
<input placeholder="{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}" type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% endblock form_widget_simple %}

Expanding on Léo's answer:
{% use 'form_div_layout.html.twig' %}
{% block widget_attributes %}
{% spaceless %}
{% set attr = attr|merge({'placeholder': label}) %}
{{ parent() }}
{% endspaceless %}
{% endblock widget_attributes %}
trans filter has been removed because it is included in the parent.

You must render the form manually.
Here's an example:
<form id="form-message" action="{{ path('home') }}" method="post" {{ form_enctype(form) }}>
{{ form_label(form.name) }}
{% if form_errors(form.name) %}
<div class="alert alert-error">
{{ form_errors(form.name) }}
</div>
{% endif %}
{{ form_widget(form.name) }}
{{ form_row(form._token) }}
<input type="submit" class="btn" value="Submit">
</form>
Related documentation

To sums it up:
Titi's answer is the most simple ;
Mick, Léo & Quolonel's answers are the most effective but are incomplete (for symfony > 2.6) :
If you use the label_format option in your *Type::configureOptions, their solution does not work. You need to add the content of the form_label block to handle all the label possibilities.
The full & most effective answer (code used w/ symfony 3.3) :
Remove form_label
{% block form_row %}
<div>
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endblock form_row %}
Edit the widget_attribute block
{% block widget_attributes %}
{% spaceless %}
{% if label is not same as(false) -%}
{% 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 -%}
{% set attr = attr|merge({'placeholder': label}) %}
{%- endif -%}
{{ parent() }}
{% endspaceless %}
{% endblock widget_attributes %}
Notes :
Do not translate the labels into the widget_attributes block, otherwise they will appear as missing translations.
The solution does not work for checkboxes or radio buttons, you'll want to add something like :
{%- block checkbox_widget -%}
{{ parent() }}
{{- form_label(form) -}}
{%- endblock checkbox_widget -%}

Bootstrap Forms
In my case best is mix aswers of #Cethy and #Quolonel Questions
{% form_theme form _self %}
{% use 'bootstrap_4_layout.html.twig' %}
{% block widget_attributes %} {# set placeholder #}
{% spaceless %}
{% set attr = attr|merge({'placeholder': label}) %}
{{ parent() }}
{% endspaceless %}
{% endblock widget_attributes %}
{% block form_row %} {# remove label #}
<div class="form-group">
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endblock form_row %}
It looks the following
It works with translations

You can also copy the labels into the placeholder attribute before rendering the form:
$formView = $form->createView();
foreach($formView->getIterator() as $item) {
/** #var $item FormView */
if ($item->vars['label']) {
$item->vars['attr']['placeholder'] =$item->vars['label'];
}
}

Related

Symfony FileType customize

I'm trying to customize the rendering of the File Input in Symfony with twig.
plugins.krajee.com/file-basic-usage-demo
I follow the documentation : https://symfony.com/doc/current/form/form_customization.html
In my config.yml
# Twig Configuration
twig:
form_themes:
- 'bootstrap_3_layout.html.twig'
I try all methode...
1) Custome inside the the view in my view.html.twig (this is a copy past of the existing bootstrap layout, I just add class="file")
{% form_theme form _self %}
{% block _user_file_widget -%}
{% if type is not defined or type not in ['file', 'hidden'] %}
{%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) -%}
{% endif %}
{%- set type = type|default('text') -%}
<input class="file" type="file" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{%- endblock _user_file_widget %}
And in my FileType
$builder->add('imageFile', FileType::class, array(
'required' => false,
'label' =>false,
'block_name' => 'file',
));
That gives me an error
The merge filter only works with arrays or "Traversable", got "NULL" as first argument.
2) Create an external file
In Ressources/View/Form/fields.html.twig
{% block form_widget_simple -%}
{% if type is not defined or type not in ['file', 'hidden'] %}
{%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) -%}
{% endif %}
{%- set type = type|default('text') -%}
<input class="file" type="file" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{%- endblock form_widget_simple %}
And in the view
{% form_theme form 'form/fields.html.twig' %}
But all fields are customized...
3) Add class in the rendering in the view
{{ form_widget(form.imageFile, {'attr': {'class': 'file', 'type': 'file'} }) }}
And for this 2 last methods I get this:
I am open to all solution !
Thank you for your help.
I found it !
It's working as I want using kartik-v/bootstrap-fileinput.
After installation, I add some assetic in my config.yml
kartik_js:
inputs:
- "%kernel.root_dir%/../vendor/kartik-v/bootstrap-fileinput/js/fileinput.js"
- "%kernel.root_dir%/../vendor/kartik-v/bootstrap-fileinput/js/locales/fr.js"
- "%kernel.root_dir%/../vendor/kartik-v/bootstrap-fileinput/themes/explorer/theme.js"
kartik_css:
inputs:
- "%kernel.root_dir%/../vendor/kartik-v/bootstrap-fileinput/css/fileinput.min.css"
- "%kernel.root_dir%/../vendor/kartik-v/bootstrap-fileinput/themes/explorer/theme.css"
Then in the view I add the css and js
{% block stylesheets %}
{{ parent() }}
{% stylesheets '#kartik_css' combine=true %}
<link href="{{ asset_url }}" type="text/css" rel="stylesheet" />
{% endstylesheets %}
{% endblock %}
{% javascripts '#kartik_js' combine=true %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
The form widget
{{ form_widget(form.imageFile)}}
And the Javascrip
$("#fos_user_profile_form_imageFile").fileinput({
language: 'fr',
showUpload: false,
allowedFileExtensions: ['jpg', 'png', 'gif']
});
The Result !
Is Amazing
I hope this will help some one :)
Have a good day !

Symfony2 errors on form field and parent

Is it possible to have validation on the form but keep it on the field at that same time? I am trying to have all my errors display at the top of the page but I would also like to have a red border around the error field. Also, each of my errors on the top should have a link that gives focus on the field that has that error.
If I use {{ form_errors(form.title) }}, I don't access to {{ form_errors(form) }}. If I use error_bubbling => true on the field then I don't access to {{ form_errors(form.title) }}. I was thinking of building my errors display in my controller, but if I do that, I'm losing Twig's functionality.
Customize your form_row block to not display error
{% block form_row %}
<div class="form-group{% if errors|length == 1 %} has-error{% endif %}">
{{ form_label(form, label|default(null)) }}
{{ form_widget(form, { 'attr' : { 'class' : 'form-control' } } ) }}
</div>
{% endblock form_row %}
Customize form_errors to be clickable. You can create a jQuery function to listen for clicks on li. In the function get the id of the clicked li remove RelatedField to get the field id
{% block form_errors -%}
{% if errors|length > 0 -%}
<ul id="allErrors">
{%- for error in errors -%}
<li id="RelatedField{{ error.id }}">{{ error.message }}</a></li>
{%- endfor -%}
</ul>
{%- endif %}
{%- endblock form_errors %}
References
How to Customize Form Rendering - http://symfony.com/doc/current/cookbook/form/form_customization.html
Here is the solution that I'm currenly using, I don't know if it's a good one. Personnaly I would rather do it all in Twig but I couldn't find a better way.
DefaultController.php : I'm passing the $errors to my view.
$errors = array();
if ($request->getMethod() == 'POST') {
$form->bind($this->getRequest());
if ($form->isValid()) {
...
} else {
foreach ($form->all() as $child) {
if (!$child->isValid()) {
$errors[$form->getName().'_'.$child->getName()] = substr($child->getErrorsAsString(),7);
}
}
}
}
template.html.twig
{% if errors|length > 0 %}
<ul>
{% for key, error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
{% block form_widget_simple %}
{% spaceless %}
{% set type = type|default('text') %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %} {% if errors|length > 0 %}class="error"{% endif %} />
{% endspaceless %}
{% endblock form_widget_simple %}
script.js
$('.errorClick').click(function() {
$('#'+$(this).attr('data-id')).focus();
});

Shortcuts with Symfony2 Twig forms : form_widget

I would like to replace:
{{ form_errors(form.name) }}
{{ form_widget(form.name, { 'attr': {'placeholder': 'Nom'} }) }}
By:
{{ form.name|field('Nom') }}
How could I do that? I tried to do it in a Twig extension but I don't have access to the form_widget function.
Edit: I could do it with the form.name properties (that include the parent form) but I would repeat symfony code, it would be a very ugly big hack
Makes more sense if you ask me to move the attr to your form class:
class SomeForm extends AbstractType {
//.....
$builder->add('name', 'text', array('attr' => array('placeholder'=>'Nom')));
}
Since i guess you need some custom rendering for some of your fields you can check:
http://symfony.com/doc/2.0/cookbook/form/form_customization.html#how-to-customize-an-individual-field
You could also create a new type and customize it as explained here:
http://symfony.com/doc/2.0/cookbook/form/form_customization.html#what-are-form-themes
You could even change the default way of rendering and ask symfony to render your placeholder tag by default using the field's label string (the details of enabling the form theme globally are covered by the link referenced above):
{% block text_widget %}
{% set type = type|default('text') %}
<input type="text" {{ block('widget_attributes') }} value="{{ value }}" />
{% endblock field_widget %}
{% block widget_attributes %}
{% spaceless %}
{% for attrname,attrvalue in attr %}{{attrname}}="{{attrvalue}}" {% endfor %} placeholder="{{ label|trans }}"
{% endspaceless %}
{% endblock widget_attributes %}
{% block form_row %}
{% spaceless %}
<div class="my-class">
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
So you would limit yourself to a form_row(form.name) using the theming that symfony provides.
Symfony's aproach looks "very" DRY/DIE to me.
Hope it helps.

How to render input inside label in Symfony2 with Twig

I try to create a custom form theme for my project where I want to render all checkbox fields INSIDE the label like:
<label><input type="checkbox" /><label>
I've found out that I have to change the choice_widget_expanded block for this:
{% block choice_widget_expanded %}
{% spaceless %}
<div {{ block('widget_container_attributes') }}>
{% for child in form %}
{{ form_widget(child) }}
{{ form_label(child) }}
{% endfor %}
</div>
{% endspaceless %}
{% endblock choice_widget_expanded %}
The problem is, when I copy the content of the form_label block into the container instead of calling form_label(child), I don't really see how the block accesses the variable passed(which was child when I called the function), and how to call the form_widget function in the form_label block:
{% block form_label %}
<label>{{ form_widget(?? what to put here??) }}</label>
{% endblock form_label %}
Also, if I create a block with a different name, like "form_label_extra" and try to call it, it throws an error, because it's not a registered twig function.
Does anyone know how this variables are passed between the form blocks, and how to achieve my goal?
I did the same modifying the following block
{% block checkbox_widget %}
{% spaceless %}
<label class="button i_clear">
<input type="checkbox"
{{ block('widget_attributes') }}
{% if value is defined %} value="{{ value }}"{% endif %}
{% if checked %} checked="checked"{% endif %} />
<span>{{ label }}</span>
</label>
{% endspaceless %}
{% endblock checkbox_widget %}
You will still need to remove the original label, you can modify it with javascript, hide it with css or modify the template so it renders conditionally, whatever is best for you.
This question might also help: Symfony2 - How to put label and input for checkboxes/radios in a same line?

How to get form variables in custom form field in symfony2

I have this custom form field
{# src/Acme/DemoBundle/Resources/views/Form/fields.html.twig #}
{% block gender_widget %}
{% spaceless %}
{% if expanded %}
<ul {{ block('widget_container_attributes') }}>
{% for child in form %}
<li>
{{ form_widget(child) }}
{{ form_label(child) }}
</li>
{% endfor %}
</ul>
{% else %}
{# just let the choice widget render the select tag #}
{{ block('choice_widget') }}
{% endif %}
{% endspaceless %}
{% endblock %}
This renders the checkboxes. But i am not able to find how can i get the value of checkbox
i.e child.form.value is not working.
Suppose i have entities which is named as tasks in the form.
how can i get the value of the taskid.
something like
child.form.vars.task.id
It seems to be in {{ choice.value }}
Have a look at this to see how the inheritance works.
<option value="{{ choice.value }}"{% if choice is selectedchoice(value) %} selected="selected"{% endif %}>{{ choice.label|trans({}, translation_domain) }}</option>

Categories