I have problem with rendering forms in Twig. I'm trying to embed a collection of forms. When I render the collection, it is not shown, while it is shown the name of form. The aim was to make the forms with an add button to add at runtime the form for each element of the collection. I get a look to the Symfony Docs and I think I followed it step by step.
This is my controller:
function new_resultAction($id)
{
$em = $this->getDoctrine()->getEntityManager();
$test = $em->getRepository('LadelaOdeskTesterBundle:Test')->find($id);
$categories =
$em->getRepository('LadelaOdeskTesterBundle:Category')->findByTest($test);
if (!$test) {
throw $this->createNotFoundException('Unable to find Test entity.');
}
$resultCategory = new Category();
$form = $this->createForm(new CategoryType() , $resultCategory);
$request = $this->getRequest();
if ('POST' === $request->getMethod()) {
$form->bindRequest($request);
if ($form->isValid()) {
$em->persist($resultCategory);
$em->flush();
$this->get('session')->setFlash('success', 'New result Categories were
saved!');
return $this->redirect($this->generateUrl('questions', array(
'id' => $resultCategory->getId(),
)));
}
}
return array(
'test' => $test,
'categories' =>$categories,
'form' => $form->createView(),
);
}
My forms:
class ResultCategoryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('value', 'integer');
$builder->add('category', 'entity', array(
'class' => 'Ladela\OdeskTesterBundle\Entity\Category',
'query_builder' => function ($repository) { return
$repository->createQueryBuilder('p')->orderBy('p.name', 'ASC'); },
'property' => 'name' ,
'expanded' => false ,
'multiple' => false , ));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Ladela\OdeskTesterBundle\Entity\ResultCategory'
));
}
public function getName()
{
return 'ResultCategory';
}
}
class CategoryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('resultCategories','collection', array(
'type' => new ResultCategoryType(),
'allow_add' => true,
'by_reference' => false,
'prototype' => true,
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Ladela\OdeskTesterBundle\Entity\Category'
));
}
public function getName()
{
return 'category';
}
}
And my twig file:
<form method="post" action="{{ path('questions',{'id': test.id }) }}" {{
form_enctype(form) }} >
<ul class="tags" data-prototype="{{
form_widget(form.resultCategories.vars.prototype)|e
}}">
{# iterate over each existing tag and render its only field: name #}
{% for ResultCategory in form.resultCategories %}
<li>{{ form_row(ResultCategory.category) }}</li>
{% endfor %}
Add a tag
</ul>
{{ form_rest(form) }}{# form.items's prototype is rendered twice #}
{{ form_errors(form) }}
<input type="submit" value ="add" />
</form>
{% block javascripts %}
<script type="text/javascript">
// Get the div that holds the collection of tags
var collectionHolder = $('ul.tags');
// setup an "add a tag" link
var $addTagLink = $('Add a tag');
var $newLinkLi = $('<li></li>').append($addTagLink);
jQuery(document).ready(function() {
// add the "add a tag" anchor and li to the tags ul
collectionHolder.append($newLinkLi);
$addTagLink.on('click', function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// add a new tag form (see next code block)
addTagForm(collectionHolder, $newLinkLi);
});
});
// Get the div that holds the collection of tags
var collectionHolder = $('ul.tags');
// setup an "add a tag" link
var $addTagLink = $('Add a tag');
var $newLinkLi = $('<li></li>').append($addTagLink);
jQuery(document).ready(function() {
// add the "add a tag" anchor and li to the tags ul
collectionHolder.append($newLinkLi);
$addTagLink.on('click', function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// add a new tag form (see next code block)
addTagForm(collectionHolder, $newLinkLi);
});
});
{% endblock %}
The collection of forms is not rendered since you don't initiate it! See the official docs. There are "dummy" tags object used to create the tag forms.
Related
I am trying to "Update field dynamicly based on the user choice in another field in Symfony 5" from "https://symfony.com/doc/current/form/dynamic_form_modification.html#form-events-submitted-data" this side. But i faced this exceptions/error "Argument 2 passed to App\Form\SportMeetupType::App\Form{closure}() must be an instance of App\Form\Sport or null, instance of App\Entity\Sport given, called in C:\Apache24\htdocs\dynamicform\src\Form\SportMeetupType.php on line 58" when ajax called.
My Code:
Form :
class SportMeetupType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('sport', EntityType::class, [
'class' => 'App\Entity\Sport',
'placeholder' => '',
])
;
$formModifier = function (FormInterface $form, Sport $sport = null) {
$positions = null === $sport ? [] : $sport->getPositions();
$form->add('position', EntityType::class, [
'class' => 'App\Entity\Position',
'placeholder' => '',
'choices' => $positions,
]);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
// this would be your entity, i.e. SportMeetup
$data = $event->getData();
$formModifier($event->getForm(), $data->getSport());
}
);
$builder->get('sport')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
// It's important here to fetch $event->getForm()->getData(), as
// $event->getData() will get you the client data (that is, the ID)
$sport = $event->getForm()->getData();
// since we've added the listener to the child, we'll have to pass on
// the parent to the callback functions!
$formModifier($event->getForm()->getParent(), $sport); // Here line 58 and error show this line
}
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SportMeetup::class,
]);
}
}
Controller:
/**
* #Route("/new", name="sport_meetup_new", methods={"GET","POST"})
*/
public function new(Request $request): Response
{
$sportMeetup = new SportMeetup();
$form = $this->createForm(SportMeetupType::class, $sportMeetup);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($sportMeetup);
$entityManager->flush();
return $this->redirectToRoute('sport_meetup_index');
}
return $this->render('sport_meetup/new.html.twig', [
'sport_meetup' => $sportMeetup,
'form' => $form->createView(),
]);
}
Twig/View:
{% extends 'base.html.twig' %}
{% block title %}New SportMeetup{% endblock %}
{% block body %}
<h1>Create new SportMeetup</h1>
{{ form_start(form) }}
{{ form_row(form.name)}}
{{ form_row(form.sport) }} {# <select id="sport_meetup_sport" ... #}
{{ form_row(form.position) }} {# <select id="sport_meetup_position" ... #}
{# ... #}
{{ form_end(form) }}
{% endblock %}
JavaScript code:
var $sport = $('#sport_meetup_sport');
// When sport gets selected ...
$sport.change(function() {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected sport value.
var data = {};
data[$sport.attr('name')] = $sport.val();
// Submit data via AJAX to the form's action path.
$.ajax({
url : $form.attr('action'),
type: "POST",
data : data,
success: function(html) {
// Replace current position field ...
$('#sport_meetup_position').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#sport_meetup_position')
);
// Position field now displays the appropriate positions.
}
});
});
When changed Sport dropdown value then ajax called and show below exceptions/error
"Argument 2 passed to App\Form\SportMeetupType::App\Form{closure}() must be an instance of App\Form\Sport or null, instance of App\Entity\Sport given, called in C:\Apache24\htdocs\dynamicform\src\Form\SportMeetupType.php on line 58"
Please help me....
Make sure that in your SportMeetupType.php you have:
use App\Entity\Sport;
I think you are using wrong class.
I'm following the tutorial at http://symfony.com/doc/current/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms
I managed to reproduce the tutorial without worries, everything works well. However, when I apply "Select2" on my fields, it does not work anymore.
Form :
class TicketAdminForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('Client', EntityType::class, [
'class' => Clients::class,
'choice_label' => 'name',
'label' => "Client",
'placeholder' => '',
'attr' => ['data-provider' => 'select2'],
])
;
$formModifier = function (FormInterface $form, Clients $client = null) {
$entity = null === $client ? [] : $client->getEntities();
$form->add('entity', EntityType::class, [
'class' => 'App\Entity\Entity',
'placeholder' => '',
'choices' => $entity,
'label' => 'Entité',
'attr' => ['data-provider' => 'select2'],
]);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
// this would be your entity, i.e. Clients
$data = $event->getData();
$form = $event->getForm();
$formModifier($event->getForm(), $data->getClient());
}
);
$builder->get('Client')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
// It's important here to fetch $event->getForm()->getData(), as
// $event->getData() will get you the client data (that is, the ID)
$client = $event->getForm()->getData();
// since we've added the listener to the child, we'll have to pass on
// the parent to the callback functions!
$formModifier($event->getForm()->getParent(), $client);
}
);
}
Twig :
{{ form_start(form, {'attr': {'class': 'form-horizontal'} }) }}
<div class="box-body">
{{ form_row(form.client) }}
{{ form_errors(form.client) }}
{{ form_row(form.entity) }}
{{ form_errors(form.entity) }}
</div>
<!-- /.box-body -->
<div class="box-footer">
<div class="col-md-offset-2 col-sm-8">
<button id="dropbutton" class="btn bg-ticketing btn-flat form-control" type="submit">
{{ 'Submit the ticket'|trans({}, 'TicketingBundle') }}
</button>
</div>
</div>
<!-- /.box-footer -->
{{ form_end(form) }}
</div>
<script>
var $client = $('#ticket_admin_form_client');
// When client gets selected ...
$client.change(function() {
console.log('dans change');
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected client value.
var data = {};
data[$client.attr('name')] = $client.val();
// Submit data via AJAX to the form's action path.
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(html) {
// Replace current entity field ...
$('#ticket_admin_form_entity').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#ticket_admin_form_entity')
);
// Entity field now displays the appropriate entities.
}
});
});
</script>
This code only works if I remove 'attr' => ['data-provider' => 'select2'] in form.
I tried :
$('#ticket_admin_form_client').on("select2:selecting", function(e) {
console.log('test');
});
but it does not work.
Do you have a solution to make it work with select2?
It's simple, in your ajax method add this :
$('#ticket_admin_form_entity').select2({});
Like this in method success by this :
// ...
$.ajax({
// ...
success: function(html) {
$('#ticket_admin_form_entity').replaceWith(
$(html).find('#ticket_admin_form_entity')
);
$('#ticket_admin_form_entity').select2({}); // Add this
}
// ...
;)
Based on documentation: http://symfony.com/doc/2.8/form/dynamic_form_modification.html#form-events-submitted-data
I prepared dynamic generated form. And everything works properly but only when I use form for adding new data (/new) when I use the same form for editing existing data - not working
Simple form for "Appointment". It should work like that: User select client and then second "select" is filling proper data - depends on each client from first select. And this works ok but only when I try add new Appointment. When I try edit no.
class AppointmentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('client', EntityType::class, array(
'class' => 'SystemAdminBundle:Client',
'placeholder' => '',
));
$formModifier = function(\Symfony\Component\Form\FormInterface $form, Client $client)
{
$diseases = array();
if($client !== null) {
$diseases = $client->getDiseases();
}
$form->add('disease', EntityType::class, array(
'class' => 'SystemAdminBundle:Disease',
'placeholder' => '',
'choices' => $diseases,
));
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
$data = $event->getData();
$formModifier($event->getForm(), $data->getClient());
}
);
$builder->get('client')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$client = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $client);
}
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'System\AdminBundle\Entity\Appointment'
));
}
}
Appointment controller - here is function for add new appointment and edit. For "new" my code works, for "edit" no.
public function newAction(Request $request)
{
$appointment = new Appointment();
$form = $this->createForm(AppointmentType::class, $appointment);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $request->request->get('appointment');
if(array_key_exists('name', $data)) {
$em = $this->getDoctrine()->getManager();
$em->persist($appointment);
$em->flush();
return $this->redirectToRoute('appointment_show', array('id' => $appointment->getId()));
}
}
return $this->render('appointment/new.html.twig', array(
'appointment' => $appointment,
'form' => $form->createView(),
));
}
public function editAction(Request $request, Appointment $appointment)
{
$deleteForm = $this->createDeleteForm($appointment);
$appointment = new Appointment();
$editForm = $this->createForm('System\AdminBundle\Form\AppointmentType', $appointment);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$data = $request->request->get('appointment');
if(array_key_exists('name', $data)) {
$em = $this->getDoctrine()->getManager();
$em->persist($appointment);
$em->flush();
return $this->redirectToRoute('appointment_show', array('id' => $appointment->getId()));
}
return $this->redirectToRoute('appointment_edit', array('id' => $appointment->getId()));
}
return $this->render('appointment/edit.html.twig', array(
'appointment' => $appointment,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
View for "new" appointment
{% block content %}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
window.onload = function() {
var $sport = $('#appointment_client');
$sport.change(function() {
var $form = $(this).closest('form');
var data = {};
data[$sport.attr('name')] = $sport.val();
data['appointment[_token]'] = $('#appointment__token').val();
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(html) {
$('#appointment_disease').replaceWith(
$(html).find('#appointment_disease')
);
}
});
});
};
{% endblock %}
View for "edit" appointment - it's almost the same as for "new" appointment
{% block content %}
{{ form_start(edit_form) }}
{{ form_widget(edit_form) }}
{{ form_end(edit_form) }}
window.onload = function() {
var $sport = $('#appointment_client');
$sport.change(function() {
var $form = $(this).closest('form');
var data = {};
data[$sport.attr('name')] = $sport.val();
data['appointment[_token]'] = $('#appointment__token').val();
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(html) {
$('#appointment_disease').replaceWith(
$(html).find('#appointment_disease')
);
}
});
});
};
{% endblock %}
You create a new Appointment in your editAction and then persist it. You should take the one that's in your function parameters, handle the request and just flush, since your object is already persisted.
So remove these lines :
$appointment = new Appointment();
// ...
$em->persist($appointment);
I'm having trouble rendering individual form fields in a twig template from a Symfony 2 form. In the example below, I'm simply trying to render the first_name form field.
Also, form_errors never renders anything.
Here is my Form object:
namespace ABC\WebsiteBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class LoanApplication extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('first_name', 'text', array('required' => true));
}
public function getName()
{
return 'LoanApplication';
}
public function getDefaultOptions(array $options)
{
return array(
//'data_class' => 'Acme\TaskBundle\Entity\Task',
'csrf_protection' => true,
'csrf_field_name' => '_token',
// a unique key to help generate the secret token
'intention' => 'loan_application',
);
}
}
Here is the controller:
namespace ABC\WebsiteBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use JML\WebsiteBundle\Form\LoanApplication;
use JML\WebsiteBundle\QuickBase as QuickBase;
class ApplyController extends Controller
{
public function indexAction()
{
$form = $this->createForm(new LoanApplication());
return $this->render('ABCWebsiteBundle:Apply:index.html.twig', array(
'form' => $form->createView(),
));
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
// perform some action, such as saving the task to the database
//return $this->redirect($this->generateUrl('task_success'));
}
}
}
}
And the twig:
<form action="{{ path('apply') }}" method="post" {{ form_enctype(form) }}>
<pre>
{{ dump(form_errors(form)) }} // always empty
</pre>
<div>
{{ form_label(form.first_name) }} // this renders
{{ form_widget(form.first_name) }} //empty
{{ form_row(form.first_name) }} //empty
</div>
<input type="submit" formnovalidate/>
</form>
i need your help please , I want to display my created form in Symfony2. I want to display my created form 92 times becouse i have 92 numbers in my database(every number is a form) , i didn't know how to do it here is my code:
controller:
class DefaultController extends Controller
{
public function QuestionsAction(Request $request)
{
$questions = $this->getDoctrine()->getEntityManager()
->getRepository('Tests\TestsPhpBundle\Entity\Question')
->findAll();
$task = new Question();
$forms = $this->createForm(new QuestionType(), $task);
if ($request->getMethod() == 'POST') {
$forms->bindRequest($request);
if ($forms->isValid())
{
$em = $this->getDoctrine()->getEntityManager();
$em->persist($task);
$em->flush();
}
}
{
return $this->render('TestsTestsPhpBundle:Default:index.html.twig', array(
'questions' => $questions,
'forms' => $forms->createView()
));
}
}
}
my form file:
class QuestionType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('categories', null, array('required' => false,
))
->add('text', 'entity', array(
'class' => 'TestsTestsPhpBundle:Question',
'query_builder' => function($repository) {
return $repository->createQueryBuilder('p')->orderBy('p.id', 'ASC'); },
'property' => 'text'))
;
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Tests\TestsPhpBundle\Entity\Question',);
}
public function getName()
{
return 'question';
}
}
my twig file:
{% block content %}
<h2>Questions</h2>
{% for question in questions %}
<dl>
<dt>Number</dt>
<dd>{{ question.number }}<dd>
{% for form in forms %}
{{ form_row(forms.categories) }}
{{ form_row(forms.text) }}
</dl>
{% endfor %}
<hr />
{% endfor %}
{% endblock %}
I recommend to read capter: Embedding Controller
http://symfony.com/doc/2.0/book/templating.html
<div id="sidebar">
{% render "AcmeArticleBundle:Article:recentArticles" with {'max': 3} %}
</div>
You can make a for loop within Twig Template and call an action (with parameter if needed) where you render the form. -> QuestionsAction in your case.