How to apply a template to one form in Symfony2? - php

Page has two forms:
<div class="row">
<div class="col-lg-6">
{{ form(form) }}
</div>
</div>
<div class="row">
<div class="col-lg-6">
{{ form(user) }}
</div>
</div>
I set a theme for the form
{% form_theme form 'AcmeDemoBundle:Form:fields.html.twig' %}
But the theme is applied to both forms
My theme
{% block form_row -%}
<div class="form-group">
{{- form_label(form) -}}
{{- form_errors(form) -}}
{{- form_widget(form, { 'attr' :{ 'class' : 'form-control' }}) -}}
</div>
{%- endblock form_row %}
{% block button_row -%}
{{- form_widget(form, { 'attr' : { 'class': 'btn btn-default' }}) -}}
{%- endblock button_row %}
But I need to theme applies only to form

Related

Form errors displayed twice

I've got a form to change a user password.
When the passwords aren't the same, the error message is displayed twice.
This is my code :
<div class="input-icon">
<i class="fa fa-lock"></i>
{{ form_widget(form.currentPassword, {'attr':{'type': 'text', 'class': 'form-control placeholder-no-fix', 'autocomplete': 'off', 'placeholder': 'sylius.form.user_change_password.current'|trans, 'name': 'current'}} ) }}
{{ form_errors(form.currentPassword) }}
</div>
And this is the result
The theme :
{%- block form_errors -%}
{%- if errors|length > 0 -%}
{%- for error in errors -%}
<div class="ui red {% if form.parent is not empty %}pointing {% endif %}label sylius-validation-error"{% if form.parent is empty %} style="margin-bottom: 10px;"{% endif %}>
{{ error.message }}
</div>
{%- endfor -%}
{%- endif -%}
{%- endblock form_errors -%}

Symfony 3: FosUserBundle Customizing registration form using custom html classes

As I was trying to customize a login form fro FosUSerBundle of my symfony 3 project, therefore I had a look on the FosUSerBundle's default twig templates in order to gen an idea, then I noticed that the twig template provided via vendor (vendor/friendsofsymfony/user-bundle/Resources/views/Registration/register_content.html.twig) has the following values:
{% trans_default_domain 'FOSUserBundle' %}
{{ form_start(form, {'method': 'post', 'action': path('fos_user_registration_register'), 'attr': {'class': 'fos_user_registration_register'}}) }}
{{ form_widget(form) }}
<div>
<input type="submit" value="{{ 'registration.submit'|trans }}" />
</div>
{{ form_end(form) }}
And I noticed that it calls a {{ form_widget(form) }} in order to render the form. so I want to know how this method is called and how can I customize the view. Basically I want the and the html classes of the form in order to look like the registration admin AdminLte's One: https://almsaeedstudio.com/themes/AdminLTE/pages/examples/register.html
Right now what I have done is to create this template app/Resources/FOSUSerBundle/views/Registration/register.html.twig:
{% extends "FOSUserBundle::layout.html.twig" %}
{% set classes='hold-transition register-page'%}
{% block fos_user_content %}
{% trans_default_domain 'FOSUserBundle' %}
<div class="register-box">
<div class="register-logo">
<h1>PhotoShare!</h1>
</div>
<div class="register-box-body">
<p class="login-box-msg">Register a new membership</p>
{{ form_start(form, {'method': 'post', 'action': path('fos_user_registration_register')}) }}
{{ form_widget(form) }}
<div class="row">
<div class="col-xs-4">
<input type="submit" class="btn btn-primary btn-block btn-flat" value="{{ 'registration.submit'|trans }}" />
</div>
</div>
{{ form_end(form) }}
</div>
{% endblock fos_user_content %}
That extends app/Resources/FOSUSerBundle/views/layout.html.twig has the following content:
{% extends '::base.html.twig' %}
{% block title %}Photoshare!!{% endblock %}
{% set classes=''%}
{% block body %}
{% block fos_user_content %}
{% endblock fos_user_content %}
{% endblock body %}
That extends the app/Resourses/views/base.html.twig template:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>{% block title %}Welcome!{% endblock %}</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
{% block stylesheets %}
<link rel="stylesheet" type="text/css" href="{{asset('assets/vendor/bootstrap/css/bootstrap.css')}}" >
<link rel="stylesheet" type="text/css" href="{{asset('assets/vendor/adminlte/adminlte.css')}}" >
<link rel="stylesheet" type="text/css" href="{{asset('assets/vendor/adminlte/skin-blue.css')}}" >
{% endblock %}
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />
{% block javascriptsHeader %}
{% endblock %}
</head>
<body class="{{ classes }}">
{% block body %}
{% endblock body %}
{% block javascriptsFooter %}
{% endblock javascriptsFooter %}
</body>
</html>
The fields I want to render are the default and the very same that are provided via the default FosUserBundle's administrator form. I want to mess around with the html classes in order to achieve the same look from the template that is mentioned above.
A form theme is what you need.
You can either create a form theme and use it only in specific templates like your registration form:
{% form_theme form 'register-form-theme.html.twig' %}
{% trans_default_domain 'FOSUserBundle' %}
{{ form_start(form, {'method': 'post', 'action': path('fos_user_registration_register'), 'attr': {'class': 'fos_user_registration_register'}}) }}
{{ form_widget(form) }}
<div>
<input type="submit" value="{{ 'registration.submit'|trans }}" />
</div>
{{ form_end(form) }}
Or you can set a global form theme in config.yml:
# Twig Configuration
twig:
# ...
form_themes:
- 'form-theme.html.twig'
Your form theme then should extend the default div layout provided by symfony:
{% extends 'form_div_layout.html.twig' %}
You can then override the blocks from this template:
{# app/Resources/views/form-theme.html.twig #}
{% extends 'form_div_layout.html.twig' %}
{%- block form_start -%}
{% set attr = attr|merge({ 'class': (attr.class|default('') ~ ' custom classes')|trim }) %}
{{ parent() }}
{%- endblock form_start -%}
{%- block form_row -%}
<div class="custom classes">
{{- form_label(form) -}}
{{- form_widget(form) -}}
{{- form_errors(form) -}}
</div>
{%- endblock form_row -%}
{%- block form_widget_simple -%}
{% set attr = attr|merge({ 'class': (attr.class|default('') ~ ' custom classes')|trim }) %}
{{ parent() }}
{%- endblock form_widget_simple -%}

Error in nested form and twig display

I have a problem with errors display in my twig templates.
Here is my twig with one nested form form.pictures :
{{ form_start(form) }}
{% if not form.vars.valid %}
<div class="flash-errors-wrapper">
{{ form_errors(form) }}
<div class="form-errors">{{ form_errors(form.pictures) }}</div>
</div>
{% endif %}
{% for formChild in form.pictures %}
<div class="child">
{% if not formChild.vars.valid %}
<div class="flash-errors-wrapper">
{{ form_errors(form) }}
<div class="form-errors">{{ form_errors(form.picture) }}</div>
<div class="form-errors">{{ form_errors(form.caption) }}</div>
</div>
{% endif %}
{{ form_widget(formChild.picture) }}
{{ form_widget(formChild.caption) }}
</div>
{% endfor %}
{{ form_end(form) }}
After submission, when a child form is non valid, my parent form is not valid too.
Problem is that it display empty div on top like :
<div class="flash-errors-wrapper">
<div class="form-errors"></div>
</div>
I don't want that because css exist on flash-errors-wrapper class so style is applied.
Any ideas?
You certainly need to check if one child form is not valid when you check if the parent form is valid. One way to do it would be (Untested, it may need some adaptation):
{% if not form.vars.valid %}
{% set all_childs_valid = True %}
{% for formChild in form.pictures %}
{% if not formChild.vars.valid %}
{% set all_childs_valid = False %}
{% endif %}
{% endfor %}
{% if all_childs_valid %}
<div class="flash-errors-wrapper">
{{ form_errors(form) }}
<div class="form-errors">{{ form_errors(form.pictures) }}</div>
</div>
{% endif %}
{% endif %}
I usually display form errors just as described in docs:
{% if errors|length > 0 %}
<ul>
{% for error in errors %}
<li>{{ error.message }}</li>
{% endfor %}
</ul>
{% endif %}

Partial User Registration on Home Page (index)

I am using Symfony2 2.6.4 with the FOS User Bundle (master) and I am having a hard time trying to pass a partial registration from the home page to the registration page.
The Goal
I want a user to start on the home page and fill out some info and press submit. I want the user to be passed to the registration page and pre fill the items that they have already filled out on the front page.
Things I have tried:
I am unsure what is feasable and have already spend too much time on this. I tried extending the registration controller but I am getting errors before I can even start on my issue so I stopped. My last thought was to pass the variables in a session to the overridden registration form type and see if I can pre-populate it there.
Has anyone done anything like this before? They sure don't make it easy.
Credit goes to #NawfalSerrar Thank you for the idea. I was close but not close enough!
So what I ended up doing was this.
This is the controller action that does that work.
/**
* #Route("/", name="Home")
*/
public function indexAction(Request $request)
{
$form = $this->createForm(new RegisterFormType());
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
$user = new User();
$session = $this->get('session');
$registration = array();
$registration['firstName'] = $data['first'];
$registration['lastName'] = $data['last'];
$registration['middleInit'] = $data['middle'];
$registration['email'] = $data['email'];
$session->set('registration', $registration);
return $this->redirect($this->generateUrl("fos_user_registration_register"));
}
return $this->render('Bundle:Page:index.html.twig', array(
'form' => $form->createView(),
));
}
Then the form on the front end.
{{ form_start(form,{'attr': {'class': 'reg-page'}}) }}
<div class="reg-header">
<h1>Get Started!</h1>
<p>Already Signed Up? Click Sign In to login
your account.</p>
</div>
{{ form_errors(form) }}
<div class="row">
<div class="col-sm-6">
{{ form_label(form.first) }}
{{ form_widget(form.first, {'attr': {'class': 'form-control margin-bottom-10'}}) }}
</div>
<div class="col-sm-6">
{{ form_label(form.middle) }}
{{ form_widget(form.middle, {'attr': {'class': 'form-control margin-bottom-10'}}) }}
</div>
<div class="col-sm-6">
{{ form_label(form.last) }}
{{ form_widget(form.last, {'attr': {'class': 'form-control margin-bottom-10'}}) }}
</div>
</div>
{{ form_label(form.email) }}
{{ form_widget(form.email, {'attr': {'class': 'form-control'}}) }}
<div class="row">
<div class="col-lg-6 text-right">
{{ form_widget(form.save, {'attr': {'class': 'btn-u'}}) }}
</div>
</div>
{{ form_end(form) }}
Registration page looks like this.
{% extends '::base.html.twig' %}
{% block body %}
{% set tokens = app.session.get('registration') %}
{% if tokens['firstName'] is defined %}
{% set firstName = tokens['firstName'] %}
{% endif %}
{% if tokens['middleInit'] is defined %}
{% set middleInit = tokens['middleInit'] %}
{% endif %}
{% if tokens['lastName'] is defined %}
{% set lastName = tokens['lastName'] %}
{% endif %}
{% if tokens['email'] is defined %}
{% set email = tokens['email'] %}
{% endif %}
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_label(form.firstName, 'First Name', {label_attr: {class: 'foo'}}) }}
{% if firstName is defined %}
{{ form_widget(form.firstName, { attr: {class: 'TestClass'}, value : firstName }) }}
{% else %}
{{ form_widget(form.firstName, { attr: {class: 'TestClass'} }) }}
{% endif %}
{{ form_label(form.middleInitial) }}
{% if middleInitial is defined %}
{{ form_widget(form.middleInitial, { attr: {class: 'TestClass'}, value : middleInit }) }}
{% else %}
{{ form_widget(form.middleInitial, { attr: {class: 'TestClass'}}) }}
{% endif %}
{{ form_label(form.lastName) }}
{% if lastName is defined %}
{{ form_widget(form.lastName, { attr: {class: 'TestClass'}, value : lastName }) }}
{% else %}
{{ form_widget(form.lastName, { attr: {class: 'TestClass'}}) }}
{% endif %}
{{ form_label(form.email) }}
{% if email is defined %}
{{ form_widget(form.email, { attr: {class: 'TestClass'}, value : email }) }}
{% else %}
{{ form_widget(form.email, { attr: {class: 'TestClass'}}) }}
{% endif %}
{{ form_label(form.plainPassword.first) }}
{{ form_widget(form.plainPassword.first) }}
{{ form_label(form.plainPassword.second) }}
{{ form_widget(form.plainPassword.second) }}
<div>
<input type="submit" value="{{ 'registration.submit'|trans }}"/>
</div>
{{ form_end(form) }}
{% endblock %}
Hope this helps!

Symfony2 Form Builder - Remove label, make it placeholder

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'];
}
}

Categories