Symfony & Easy Admin : How to apply easyadmin template to custom form - forms

I created a custom form for and integrated it in easyadmin. the forms is displayed, filled and action is working, but the templating is not good :
Here is my Twig :
{% extends "#EasyAdmin/page/content.html.twig" %}
{% form_theme form with easyadmin_config('design.form_theme') only %}
{% block body_id 'easyadmin-edit-User-1' %}
{% block body_class 'edit edit-user' %}
{% block content_title %}
<h1 class="title">Edit Account</h1>
{% endblock %}
{% block content_footer_wrapper '' %}
{% block main %}
{% block entity_form %}
{{ form_start(form) }}
{{ form_widget(form) }}
<button type="submit" class="btn btn-primary">Update</button>
{{ form_end(form) }}
{% endblock entity_form %}
{% endblock %}
{% block body_javascript %}
{{ include('#EasyAdmin/default/includes/_select2_widget.html.twig') }}
{% endblock %}
and my controller :
class UserController extends EasyAdminController
{
public function editaccountAction(UserInterface $loggedUser, Request $request) {
$repository = $this->getDoctrine()->getRepository(User::class);
$id = $loggedUser->getId();
$entity = $repository->find($id);
$form = $this->createForm(UserType::class, $entity);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** #var Article $article */
$entity= $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
$this->addFlash('success', 'Account Saved');
return $this->redirectToRoute('easyadmin');
}
return $this->render('user/editaccs.html.twig', [
'form' => $form->createView(),
]);
}
}
How can I have the same form presentation as edit?

Before
{{ form_start(form) }}
add
{% form_theme form '#EasyAdmin/form/bootstrap_4.html.twig' %}
https://symfony.com/doc/current/form/form_themes.html#applying-themes-to-single-forms
UPD:
In EasyAdmin 4.3 theme would be
{% form_theme form '#EasyAdmin/crud/form_theme.html.twig' %}

Related

How can I put the asterisk of my required field on my label? (Symfony form)

I am working on Symfony 3 and I have some trouble with my form.
When I create a Symfony form with a field not required, here is my code :
I create the form :
$form = $this->createFormBuilder()
->add('prenom' TextType::class, array(
'label' => 'Votre prénom',
'required' => false
)
->getForm();
Here is the code in my view for this field:
{{ form_label(form.prenom) }}
{{ form_errors(form.prenom) }}
{{ form_widget(form.prenom) }}
And this is the HTML I have :
<label class="control-label" for="contact_prenom">Votre prénom</label>
<input type="text" id="contact_prenom" name="contact[prenom]" class="form-control"/>
Now if I do the same without the 'require' => false on my FormBuilder, here is the HTML I get:
<label class="control-label required" for="contact_prenom">Votre prénom</label>
<sup class="required" title="Champ obligatoire">
<i class="fa fa-asterisk"></i>
</sup>
<input type="text" id="contact_prenom" name="contact[prenom]" required="required" class="form-control" />
Is it possible to control the "sup" tag so the asterisk * can be with my label?
I guess I can do it with jQuery, but I'd like to know if it is possible to do it on my form builder or in Twig?
In the doc there is a specific section here http://symfony.com/doc/current/form/form_customization.html#adding-a-required-asterisk-to-field-labels
You can even do with CSS only
label.required:before {
content: "* ";
}
As of Symfony 5.1 you can do the following
->add('name', TextType::class, [
'label' => 'Name <span class="badge badge-danger badge-pill">Required</span>',
'label_html' => true
])
The label_html (bool) property will allow HTML to be injected into the label directly and render on the form output.
Documentation - https://symfony.com/doc/current/reference/forms/types/form.html#label-html
Yes, you could override the twig template or the block that symfony uses to render your widget, have a look at:
http://symfony.com/doc/current/templating/overriding.html
In your case, you're looking for
vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
And this would be the block you want to override:
{%- block form_label -%}
{% if label is not same as(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 -%}
<label{% if label_attr %}{% with { attr: label_attr } %}{{ block('attributes') }}{% endwith %}{% endif %}>{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}</label>
{%- endif -%}
{%- endblock form_label -%}

Symfony-Twig: insert fontawesome icon in a form_widget

To validate a form I am using a standard:
{{ form_widget(form.save, {'attr': {'class': 'btn btn-sm btn-danger'}, 'label': 'Submit form'}) }}
I want to insert a fontawsome icon in the button. I tried:
{{ form_widget(form.save, {'attr': {'class': 'btn btn-sm btn-danger'}, 'label': '<i class="fa fa-envelope-o"></i> Submit form'}) }}
But it is not working; obviously
Any idea how to that?
I would define a new form template in the same view (or in a template if you need to reuse the code). More details here
{% extends '::base.html.twig' %}
{% form_theme form _self %}
{%- block submit_widget -%}
{%- set type = type|default('submit') -%}
{%- 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') }}>
<i class="fa fa-envelope-o"></i>
{{ label|trans({}, translation_domain) }}
</button>
{%- endblock submit_widget -%}
{% block content %}
{# ... render the form #}
{{ form_row(form.age) }}
{% endblock %}
EDIT
You can also extend ButtonType to allow icon_before and icon_after in order to add icons easily in form definition :
$form->add('submitReportV2Show', SubmitType::class, array(
'label' => 'My test',
'icon_before' => 'fa-refresh',
'icon_after' => 'fa-refresh',
'attr' => array('class' => 'btn btn-sm btn-success'
)));
Create a new class src/bundle/Form/Extension:
namespace YourBundle\ToolBoxBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
class IconButtonExtension extends AbstractTypeExtension
{
public function getExtendedType()
{
return ButtonType::class;
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['icon_before'] = $options['icon_before'] ?? '';
$view->vars['icon_after'] = $options['icon_after'] ?? '';
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'icon_before' => null,
'icon_after' => null
]);
}
}
Declare it in service src/bundle/Resources/config/service.yml
bundle.tools.form.type_extension.icon_button:
class: YourBundle\ToolBoxBundle\Form\Extension\IconButtonExtension
tags:
- { name: 'form.type_extension', extended_type: 'Symfony\Component\Form\Extension\Core\Type\ButtonType' }
app/Resources/views/Form/fields.html.twig
{%- 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 same as(false) -%}
{% set translation_domain = false %}
{%- else -%}
{% set label = name|humanize %}
{%- endif -%}
{%- endif -%}
<button type="{{ type|default('button') }}" {{ block('button_attributes') }}>
{% if icon_before is defined and icon_before is not null %}
<i class="fa {{ icon_before }}"></i>
{% endif %}
{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}
{% if icon_after is defined and icon_after is not null %}
<i class="fa {{ icon_after }}"></i>
{% endif %}
</button>
{%- endblock button_widget -%}
sdespont's answer is the correct answer and worthy of being selected. I have, however, extended the functionality of it to include adding the custom fa class icon as well as whether the icon is placed to the left or right of the button text.
Since this functionality accepts variables the best thing to do is to create a template to be reused instead of customising just the view.
Form template: app/Resources/views/form/submit.html.twig
{# app/Resources/views/form/submit.html.twig #}
{% block submit_widget %}
{% set type = type|default('submit') %}
{% 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') }}>
{% if fa is defined %}
{% if left is defined and left %}
<i class="fa {{ fa }}"></i>
{% endif %}
{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}
{% if right is defined and right %}
<i class="fa {{ fa }}"></i>
{% endif %}
{% else %}
{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}
{% endif %}
</button>
{% endblock submit_widget %}
Controller:
$form = $this->createFormBuilder($user)
...
->add('submit', SubmitType::class, array(
'attr'=> array('class'=>'myclass')
))
->getForm();
Twig template:
{{ form_widget(form.submit, {'fa' : 'fa-long-arrow-right','right' : true}) }}
You can set any old fa icon and even sizing like so: fa-long-arrow-right fa-2x
The easiest, you can put your button with html and form vars:
<button type="submit" name="{{ form.send.vars.full_name }}" id="{{ form.send.vars.id }}" class="btn btn-sm btn-danger"><i class="fa fa-envelope-o"></i></button><
You can just add a new custom service css class per icon
/*
* css selector for a class attribute that starts with "btn-fa-" or has " btn-fa-" in it:
*/
[class^="btn-fa-"]:before,
[class*=" btn-fa-"]:before
{
font-family: "Font Awesome 5 Free";
font-weight: bold;
margin: 0 6px 0 2px;
}
/*
* And then only 1 setting per font awesome class
*/
.btn-fa-plus:before {
content: '\f067';
}
And add the class to the ButtonType
->add('Add an item', ButtonType::class, [
'attr' => [
'class' => 'btn btn-primary btn-fa-plus',
]
])
In YourFormType.class -> buildForm
->add('submit', SubmitType::class, [
'attr' => [
'class' => 'main-btn primary-btn',
],
'label' => '<i class="fas fa-search"></i> Search',
'label_html' => true,
])

Symfony2 Multiple forms send by one submit button

I need help. I've created view, where I'm creating multiple forms for one type of object. Now i want to save the forms, all with one button and then persist those objects to database.
Here is the controller:
/**
* #Route("/project/{project_id}/string/{id}/edit/", name="StringEdit")
* #Template()
*/
public function editAction($project_id, $id, Request $request)
{
$string = $this->getDoctrine()->getRepository('DomestosTranslatingBundle:String')->find($id);
$translations = $this->getDoctrine()->getRepository('DomestosTranslatingBundle:Translation')->findByString($string);
//$form = $this->createForm(new TranslationType(), $translation);
//$form->handleRequest($request);
$forms = array();
foreach($translations as $translation){
$form = $this->createForm(new TranslationType, $translation);
$form = $form->createView();
$forms[] = $form;
}
return $this->render('DomestosTranslatingBundle:String:edit.html.twig', array(
'forms' => $forms,
'string' => $string,
));
}
And the view:
{% extends "::base.html.twig" %}
{% block title %}Edit translations{% endblock %}
{% block body %}
Code: {{string.code}}
<p>
<table>
{% for keylang,lang in string.project.lang %}
{% for key,form in forms %}
{% if key == keylang %}
<tr>
<td>{{lang.title}}</td>
<td>{{form_widget(form.text)}}</td>
</tr>
{% endif %}
{% endfor %}
{% endfor %}
</table>
<p>
{% endblock %}
No, you can't do this. Only one form could be submitted at one time.
Instead of using an array of forms, you could create one form and Embed a Collection of Forms.

Customize form field rendering

I would like to customize the rendering of a form field in the edit page from sonata admin bundle to include an applet that uses the text content of a field.
I know that I have to edit the configureFormFields function in the admin class, but I need to know 3 things:
What is the syntax to provide a field form template
Where to put the template file ( which directory )
What the template have to looks like.
Found a solution
What i have done is:
Created a field type, lets call it myfieldType in myCompany\myBundle\Form\Type\myfieldType.php
namespace myCompany\myBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class myfieldType extends AbstractType
{
public function getParent()
{
return 'text';
}
public function getName()
{
return 'myfield';
}
}
Registered the Type in app/config/services.yml
myCompany.myBundle.form.type.myfield:
class: myCompany\myBundle\Form\Type\myfieldType
tags:
- { name: form.type, alias: myfield }
In my myentityAdmin class,
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('myfieldname', 'myfield')
...
}
and
public function getFormTheme() {
return array('myCompanymyBundle:Admin:myfield_edit.html.twig');
}
and the template :
{# src/mycompany/myBundle/Resources/views/Form/myfield_edit.html.twig #}
{% block myfield_widget %}
{% spaceless %}
{{ block('textarea_widget') }}
{% endspaceless %}
{% endblock %}
And now i can access the form field value by the twig variable "value" !
So easy... when you got it.
user1254498's solution won't work unless the block name prefix matches the name of the form type. At least with the last version of sonata admin bundle (2.2.12). In this case:
{# src/mycompany/myBundle/Resources/views/Form/myfield_edit.html.twig #}
{% block myfield_widget %}
{% spaceless %}
{{ block('textarea_widget') }}
{% endspaceless %}
{% endblock %}
And, regarding getFormTheme(), you shoud return also the parent theme, otherwise you may break the whole style...
public function getFormTheme()
{
return array_merge(
parent::getFormTheme(), array(
'mycompanyBundle:Form:myfield_edit.html.twig')
);
}
Also, you can access the admin service in the twig template with the variable sonata_admin.admim.
In your services.yml file you define the template for your edit Action:
app.admin.product:
class: AppBundle\Admin\ProductAdmin
arguments: [~, AppBundle\Entity\Product, AppBundle:Admin\Product]
tags:
- {name: sonata.admin, manager_type: orm, group: Products, label: Products}
calls:
- [ setTemplate, [edit, AppBundle:Product:edit.html.twig]]
In that template you can then override templates for fields in your form:
{% extends 'SonataAdminBundle:CRUD:base_edit.html.twig' %}
{% form_theme form.selectall 'AppBundle:Form:selectall.html.twig' %}
{% form_theme form.Country 'AppBundle:Form:country.html.twig' %}
Then my template looks like that:
{% block form_row %}
<div class="form-group">
{{ form_label(form) }}
{% set c = 0 %}
{% for i in form %}
{% set c = c+1 %}
{% if (c == 1) %}
<div style="float: left; width: 20%;">
{% endif%}
{{ form_row(i) }}
{% if ((c == 60) or (form|length == loop.index)) %}
</div>
{% set c = 0 %}
{% endif%}
{% endfor %}
</div>
{% endblock form_row %}
In this case, my countries check boxes appear in column of 60 elements, not in one column with the whole list of elements.
Hope this is helpful to someone else.

Symfony2: Form fragment themeing and TWIG

Can anyone explain why this code:
{% form_theme form _self %}
{% block avo_gallery_upload_widget %}
{% spaceless %}
<label for="name">Name:</label>
{{ form_widget(form.name) }}
<label for="description">Description:</label>
{{ form_widget(form.description) }}
{% endspaceless %}
{% endblock avo_gallery_upload_widget %}
Throws
Method "name" for object "Symfony\Component\Form\FormView" does not exist in MyBundle:Default:upload.html.twig at line 13
For reference: line 13 is {{ form_widget(form.name) }}
But when wrapped in IF clause:
{% form_theme form _self %}
{% block avo_gallery_upload_widget %}
{% spaceless %}
{% if form.name is defined %}
<label for="name">Name:</label>
{{ form_widget(form.name) }}
<label for="description">Description:</label>
{{ form_widget(form.description) }}
{% endif %}
{% endspaceless %}
{% endblock avo_gallery_upload_widget %}
Suddenly everything works fine!
For reference - this is how form looks like:
class GalleryUploadType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('name', 'text')
->add('description', 'textarea')
;
}
public function getName()
{
return 'avo_gallery_upload';
}
public function getDefaultOptions(array $options){
return array('data_class' => 'Me\MyBundle\Entity\GalleryUpload');
}
}
fabpot closed github issue (2012-07-03) with comment:
Every month, I spend hours trying to reproduce the problem without luck. So, I'm giving up for now as there is probably something else going on in your application (as it works fine for almost everyone). If you have any new information that can be relevant, feel free to reopen a new ticket. Thanks.
Since it probably is something wrong in my application and there is nothing new I could add to the question I am closeing this question.
If you happen to encounter this error have a look at this workaround.
If you have any additional info on reproducing the problem, post it here.