Skip to content

Instantly share code, notes, and snippets.

@tentacode
Last active August 29, 2015 14:01
Show Gist options
  • Select an option

  • Save tentacode/977cf36aba3e709411e1 to your computer and use it in GitHub Desktop.

Select an option

Save tentacode/977cf36aba3e709411e1 to your computer and use it in GitHub Desktop.
PrototypeCollectionType is a new type I made to easily, generically, manage collection form with prototypes (add + remove), in a nicer way (IMO) than in the Symfony cookbook.
{% extends 'TentacodeLolcatBundle::layout.html.twig' %}
{% form_theme form 'TentacodeLolcatBundle:Form:form_theme.html.twig' %}
{% block content %}
{{ form(form) }}
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script src="{{ asset('bundles/tentacodelolcat/js/prototype_collection.js') }}"></script>
{% endblock %}
<?php
// src/Tentacode/LolcatBundle/Form/CrazyCatLadyType.php
namespace Tentacode\LolcatBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class CrazyCatLadyType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('lonelynessIndex')
->add('numberOfCatToys')
->add('cats', 'prototype_collection', [
'type' => new CatType(),
'label' => 'How many cats is too much cats ?',
'add_label' => "Add another cat like she does not have enought already",
])
->add('save', 'submit')
;
}
public function getName()
{
return 'crazy_cat_lady';
}
}
{# src/Tentacode/LolcatBundle/Resources/views/Form/form_theme.html.twig #}
{% macro prototype_child_widget(child, index, disabled = false) %}
<div class="prototype-collection-child" data-index="{{ index }}">
{{ form_widget(child) }}
{% if not disabled %}
<a class="prototype-collection-delete" href="#">delete</a>
{% endif %}
</div>
{% endmacro %}
{% block prototype_collection_row %}
<div class="prototype-collection" id="{{ form.vars.id }}">
{% if label is not sameas(false) %}
<h5>{{ label }}</h5>
{% endif %}
{% if not disabled %}
<script class="prototype" type="text/template">
{{ _self.prototype_child_widget(prototype, '__name__') }}
</script>
{% endif %}
{% for childIndex, child in form %}
{{ _self.prototype_child_widget(child, childIndex, disabled) }}
{% endfor %}
{% if not disabled %}
<a class="prototype-collection-add" href="#">{{ add_label }}</a>
{% endif %}
</div>
{% endblock %}
// sorry ! jQuery inside !
// src/Tentacode/LolcatBundle/Resources/public/js/prototype_collection.js
$(document).ready(function(){
//removing an element
$('body').on('click', '.prototype-collection-delete', function(e){
e.preventDefault();
$(this).parents('.prototype-collection-child').remove();
})
//adding a new element
$('body').on('click', '.prototype-collection-add', function(e){
e.preventDefault();
var $collection = $(this).parents('.prototype-collection');
var prototypeHtml = $collection.find('.prototype').html();
prototypeHtml = prototypeHtml.replace(/__name__/g, getNextIndex($collection));
$(this).before(prototypeHtml);
})
})
// ensure having a correct index, just using length is not sufficient
function getNextIndex($collection)
{
var lastIndex = -1;
var items = $collection.find('.prototype-collection-child');
$(items).each(function(i, item) {
var itemIndex = $(item).attr('data-index');
if (itemIndex > lastIndex) {
lastIndex = parseInt(itemIndex);
}
});
return lastIndex + 1;
}
<?php
// src/Tentacode/LolcatBundle/Form/PrototypeCollectionType.php
namespace Tentacode\LolcatBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class PrototypeCollectionType extends AbstractType
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['add_label'] = $options['add_label'];
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'add_label' => 'Add an item',
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'cascade_validation' => true
));
}
public function getName()
{
return 'prototype_collection';
}
public function getParent()
{
return 'collection';
}
}
# src/Tentacode/LolcatBundle/Resources/config/services.yml
services:
tentacode.form.prototype_collection_type:
class: Tentacode\LolcatBundle\Form\PrototypeCollectionType
tags:
- { name: form.type, alias: prototype_collection }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment