Multilevel dynamic forms in Symfony - symfony

I need to create a form adding faculty to the database. First, the user selects a region from the list ( / ChoiceType), then the city of this region from the following list, the university, and finally enters the name of the faculty. The default values are the first region from the database, its first city and first university.
Sending the page with default data works, the choice of the region works, but the the choice of the city return to 500 status
Form:
Twig and ajax:
{% extends 'admin/insert/insert.html.twig' %}
{% block title %}Add Faculty{% endblock %}
{% block body %}
<div class="insert">
<h1 class="insert__title">Add Faculty</h1>
{{ form_start(insert_faculty, { 'attr' : {'class' : 'insert__form'} }) }}
{% for message in app.flashes('success') %}
<div class="insert__success">
{{ message }}
</div>
{% endfor %}
<div class="insert__errors">
{{ form_errors(insert_faculty) }}
</div>
{{ form_label(insert_faculty.region, 'Region:', { 'label_attr' : {'class' : 'insert__label'} }) }}
{{ form_widget(insert_faculty.region, { 'attr' : {'class' : 'insert__input'} }) }}
{{ form_label(insert_faculty.city, 'City:', { 'label_attr' : {'class' : 'insert__label'} }) }}
{{ form_widget(insert_faculty.city, { 'attr' : {'class' : 'insert__input'} }) }}
{{ form_label(insert_faculty.university, 'University:', { 'label_attr' : {'class' : 'insert__label'} }) }}
{{ form_widget(insert_faculty.university, { 'attr' : {'class' : 'insert__input'} }) }}
{{ form_label(insert_faculty.name, 'Name:', { 'label_attr' : {'class' : 'insert__label'} }) }}
{{ form_widget(insert_faculty.name, { 'attr' : {'class' : 'insert__input insert__input_name'} }) }}
<button type="submit" class="insert__button">Save</button>
{{ form_end(insert_faculty) }}
<div class="insert__buttons">
Back
</div>
</div>
{% block javascripts_footer %}
{{ parent() }}
<script>
let $region = $('#insert_faculty_region');
$region.change(function() {
let $form = $(this).closest('form');
let data = {};
data[$region.attr('name')] = $region.val();
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(get) {
$('#insert_faculty_city').html(
$(get).find('#insert_faculty_city').html()
);
$('#insert_faculty_university').html(
$(get).find('#insert_faculty_university').html()
);
}
});
});
let $city = $('#insert_faculty_city');
$city.change(function() {
let $form = $(this).closest('form');
let data = {};
data[$city.attr('name')] = $city.val();
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(get) {
$('#insert_faculty_university').html(
$(get).find('#insert_faculty_university').html()
);
}
});
});
</script>
{% endblock %}
{% endblock %}
Form class:
class InsertFacultyType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('region', ChoiceType::class, [
'choices' => $options['regions_array'],
'mapped' => false,
])
->add('city', ChoiceType::class, [
'choices' => null,
'mapped' => false,
])
->add('university', ChoiceType::class, [
'choices' => null,
])
->add('name')
;
$formModifier = function (FormInterface $form, $entity_parent) {
if (get_class($entity_parent) === 'App\Entity\Region') {
if (!$entity_parent->getCities()->count()) {
$form->add('city', ChoiceType::class, [
'choices' => null,
'mapped' => false,
]);
}
else {
$cities_in_database = $entity_parent->getCities();
foreach ($cities_in_database as $city) {
$cities[$city->getName()] = $city;
}
$form->add('city', ChoiceType::class, [
'choices' => $cities,
'mapped' => false,
]);
}
}
else if (get_class($entity_parent) === 'App\Entity\City') {
if (!$entity_parent->getUniversities()->count()) {
$form->add('university', ChoiceType::class, [
'choices' => null,
]);
}
else {
$university_in_database = $entity_parent->getUniversities();
foreach ($university_in_database as $university) {
$universities[$university->getName()] = $university;
}
$form->add('university', ChoiceType::class, [
'choices' => $universities,
]);
}
}
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($options, $formModifier, $builder) {
$region = $options['regions_array'][array_key_first($options['regions_array'])];
$city = $region->getCities()[0];
$formModifier($event->getForm(), $region);
$formModifier($event->getForm(), $city);
}
);
$builder->get('region')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$region = $event->getForm()->getData();
$city = $region->getCities()[0];
$formModifier($event->getForm()->getParent(), $region);
$formModifier($event->getForm()->getParent(), $city);
}
);
$builder->get('city')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$city = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $city);
}
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Faculty::class,
'regions_array' => null,
]);
$resolver->setAllowedTypes('regions_array', 'array');
}
}
Controller:
/**
* #Route("/admin/insert/faculty", name="faculty")
*/
public function faculty(Request $request)
{
$regions_in_database = $this->getDoctrine()->getRepository(Region::class)->findAll();
$regions = [];
foreach ($regions_in_database as $region) {
$regions[(string)$region->getName()] = $region;
}
$faculty = new Faculty();
$insert_faculty = $this->createForm(InsertFacultyType::class, $faculty, [
'regions_array' => $regions,
]);
if (!$regions_in_database) {
$insert_faculty->addError(new FormError("There are no regions!"));
}
$insert_faculty->handleRequest($request);
if ($insert_faculty->isSubmitted() && $insert_faculty->isValid()) {
$repository = $this->getDoctrine()->getRepository(University::class);
$faculty_in_database = $repository->findOneBy(
[
'name' => $faculty->getName(),
'university' => $faculty->getUniversity(),
]
);
if ($faculty_in_database) {
$insert_faculty->addError(new FormError('Such a faculty is already in the database!'));
}
else {
$faculty->setRating(0);
if(!$faculty->getUniversity()) {
$insert_faculty->addError(new FormError("Select the university!"));
}
else {
$entity_manager = $this->getDoctrine()->getManager();
$entity_manager->persist($faculty);
$entity_manager->flush();
$this->addFlash(
'success',
'Faculty "' . $faculty->getName() . '" successfully saved!'
);
}
}
}
return $this->render('admin/insert/faculty/faculty.html.twig', [
'insert_faculty' => $insert_faculty->createView(),
]);
}

I invite you to use the Symfony debug toolbar. It will allow you to see the different problems associated with your code and give much more information about the problems that appear.
Profiler Symfony
About your problem, I think it is necessary to debug at the level of what your form sends to your application. But if you want more help, you must provide the error message that come with your 500 error.

Related

Dynamically populate project drop down data based on client drop down choice data. But edit form show exception/error in symfony 5

I want to "Dynamically populate project drop down data based on client drop down choice data" in symfony 5. New page work properly, but when change client drop down value from edit page then ajax called and show this exception/error: "Expected argument of type "string", "null" given at property path "buildingPosition"."
Form Code:
class EnquiryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('client', EntityType::class, [
// looks for choices from this entity
'class' => Client::class,
// uses the User.username property as the visible option string
'choice_label' => 'name',
'placeholder' => 'Select a Client...',
// 'label' => 'Contact Person Designation',
// used to render a select box, check boxes or radios
// 'multiple' => true,
// 'expanded' => true,
])
->add('buildingPosition', TextType::class, [
'attr' => ['class' => 'form-control'],
])
->add('offerSubmissionDate', DateType::class, [
'attr' => ['class' => 'form-control'],
'widget' => 'single_text',
'empty_data' => null,
])
->add('probability', ChoiceType::class, [
'choices' => [
'P1' => 'P1',
'P2' => 'P2',
'P3' => 'P3',
],
'placeholder' => 'Select a Probability...',
])
->add('notes', TextType::class, [
'attr' => ['class' => 'form-control'],
])
;
// Start Project data populate dynamically based on the value in the "client" field
$formModifier = function (FormInterface $form, Client $client = null) {
$projects = null === $client ? [] : $client->getProjects();
$form->add('project', EntityType::class, [
'class' => 'App\Entity\Project',
'placeholder' => '',
'choices' => $projects,
]);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
// this would be your entity, i.e. Enquiry
$data = $event->getData();
$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);
}
);
// End Project data populate dynamically based on the value in the "client" field
}
Controller Code:
public function edit(Request $request, $id): Response
{
$enquiry = new Enquiry();
$entityManager = $this->getDoctrine()->getManager();
$enquiry = $entityManager->getRepository(Enquiry::class)->find($id);
$form = $this->createForm(EnquiryType::class, $enquiry);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->flush();
$this->addFlash('notice', 'Enquiry updated successfully!');
return $this->redirectToRoute('enquiry_index');
}
return $this->render('enquiry/edit.html.twig', [
'enquiry' => $enquiry,
'form' => $form->createView(),
]);
}
Twig/View Code:
{{ form_start(form) }}
{{ form_row(form.client, {'attr': {'class': 'form-control'}}) }}
{{ form_row(form.project, {'attr': {'class': 'form-control'}}) }}
{{ form_row(form.buildingPosition) }}
{{ form_row(form.offerSubmissionDate) }}
{{ form_row(form.probability, {'attr': {'class': 'form-control'}}) }}
{{ form_row(form.notes) }}
{{form_row(row)}}
JavaScript Code:
var $client = $('#enquiry_client');
// When sport gets selected ...
$client.change(function() {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected sport 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: "POST",
data : data,
success: function(html) {
// Replace current position field ...
$('#enquiry_project').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#enquiry_project')
);
// Position field now displays the appropriate positions.
}
});
});
Please help me.....

Symfony 4 - Dynamic Generation for Submitted Forms with Select2

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
}
// ...
;)

Checkbox for multiple values throws exception for wrong type array given

I tried some things and I am unable to add a multiple checkbox. When I submit the form I get this error message:
Expected argument of type "string or null", "array" given at property path "caractere_emotion".
My PersonnageType.php
->add('caractere_emotion', ChoiceType::class, [
'required' => false,
'label' => 'Émotions',
'choices' => [
'Actif' => 'Actif',
'Créatif' => 'Créatif',
'Génie' => 'Génie',
'Créatif' => 'Créatif',
'Joyeux' => 'Joyeux',
'Morose' => 'Morose',
'Pitre' => 'Pitre',
'Romantique' => 'Romantique',
'Sang Chaud' => 'Sang Chaud',
'Sûr de lui' => 'Sûr de lui',
],
'expanded' => true,
'multiple' => true,
])
My Personnage.php entity :
/**
* #ORM\Column(type="array", nullable=true)
*/
private $caractere_emotion;
CharacterController.php
class CharacterController extends AbstractController
{
public function index(Request $request)
{
$em = $this->getDoctrine()->getManager();
$personnage = new Personnage();
$form = $this->createForm(PersonnageType::class, $personnage);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($personnage);
$em->flush();
}
return $this->render('character/index.html.twig', [
'form' => $form,
'form' => $form->createView()
]);
}
}
Template :
{{ form_start(form) }}
{{ form_row(form.competences) }}
{{ form_row(form.principes) }}
{{ form_row(form.autres_personnalite) }}
{{ form_row(form.caractere_emotion) }}
{{ form_row(form.caractere_loisir) }}
<button type="submit" class="btn btn-success">Terminer</button>
{{ form_end(form) }}

Symfony2 - Dynamic generated form not working while editing form

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);

How to render collections of forms

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.

Resources