Symfony 2 Form, field collection doesn't display
I would like to create a form which can add multiple etape. I create the form like this:
//FORM
namespace RBO\TryBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use RBO\TryBundle\Entity\Try;
class TryType extends AbstractType {
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('etapes', 'collection', array(
'type' => new EtapeType(),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'label' => 'Etapes'
));
}
public function getName()
{
return 'try';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'RBO\TryBundle\Entity\Try',
'csrf_protection' => true,
'csrf_field_name' => '_token',
// a unique key to help generate the secret token
'intention' => 'try_item',
);
}
}
// EtapeType
<?php
namespace RBO\TryBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use RBO\TryBundle\Entity\Etape;
class EtapeType extends AbstractType {
public function bu开发者_运维问答ildForm(FormBuilder $builder, array $options)
{
$builder->add('name', 'text');
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'RBO\TryBundle\Entity\Etape',
);
}
public function getName()
{
return 'etape';
}
}
// Display in twig template
{{ form_row(form.etapes) }}
The Entity Try have a property etapes which is an ArrayCollection (defined in the constructor)
This code render nothing expect the label. Did I miss something?
Thank you by advance
I have always used form_widget to render embedded forms, but I think form_row will work fine too. The important thing you should look at is if the embedded form container has a "data-prototype" attribute. Then, you must add a script to the page where you are displaying the form. In my case, I use Mootools, but I gues you can easily translate this script to jQuery or any other javascript framework:
These are the contents of my js file (you should replace "XXX" for the id of the element where the "data-prototype" attribute is:
window.addEvent( 'domready', function() {
var add = function() {
var collectionHolder = $('XXX');
var prototype = collectionHolder.getAttribute('data-prototype');
form = prototype.replace(/\$\$name\$\$/g, collectionHolder.getChildren().length);
var cont = Elements.from(form);
collectionHolder.adopt( cont );
}
var remove = function() {
var collectionHolder = $('XXX');
var child = collectionHolder.getLast();
child.dispose();
}
$$('a.add-link').addEvent('click', function(e){
e.stop();
add();
});
$$('a.remove-link').addEvent('click', function(e){
e.stop();
remove();
});
});
This script is very simple, and just adds new elements or removes the last element in the collection. Besides, you should add some html to your view in order to have add/remove links:
<ul class="actions">
<li>
<a href="#" class="add-link">
Add
</a>
</li>
<li>
<a href="#" class="remove-link">
Remove last
</a>
</li>
</ul>
I hope it helps, I'm new to Symfony and I'm still learning, but it worked for me :)
I would do it in a such way:
{% for etape in form.etapes %}
{{ form_widget(etape.name) }}
{% endfor %}
I could be wrong, because I don't use twig-templates. But this idea worked for me in php-templates.
In PHP
Templates..
`<?php foreach ($form['etapes'] as $etapesField) : ?>
<?php echo $view['form']->errors($etapesField) ?>
<?php echo $view['form']->widget($etapesField) ?>
<?php endforeach; ?>`
Ok, two things:
1 - The regular way to display your form in a twig template:
{{ form_row(form) }}
The templating system is able to cascade (sorry for the approximate english), meaning that all sub-fields in a form will be rendered as well as its encapsulating form. Thus, no need to render the children directly by calling form.etapes.
2 - I believe your problem really lies in the instanciation. If you create your form that way:
$form = $this->createForm(new TryType()));
Or even that way:
$model = new Try();
$model->setEtapes(array());
$form = $this->createForm(new TryType(), $model));
Then it is normal that your form renders nothing, because the number of Etape fields that it displays directly depends on the amount of Etape instances you feed your model with. If you provide it an empty array, no Etape fields will be rendered. What you should do to have an empty single field when displaying it is:
$model = new Try();
$model->setEtapes(array(new Etape())); // Empty Etape
$form = $this->createForm(new TryType(), $model));
You can thusly add as many fields as you please in your collection type:
$model = new Try();
$model->setEtapes(array(
new Etape('Étape 1'),
new Etape('Étape 2'),
...
));
$form = $this->createForm(new TryType(), $model));
Since you are using a sub-form, do this;
$builder
->add(
'etapes',
'collection',
array(
'type' => new EtapeType(),
'allow_add' => true,
'allow_delete' => true,
)
)
->getForm()
;
And how you display in twig doesn't really matter. It depends on what you want. form_row, form_widget all should work properly.
精彩评论