merged branch marcw/form-prototype-attribute (PR #1724)

Commits
-------

257f86c [Form] Collection's prototype is not not a child anymore.
2b4cc9b [Form] Changed collection prototype rendering.

Discussion
----------

[Form] Changed collection prototype rendering

Based on PR #1500. It is now rendered inside an attribute of collection
tag.

The data-attribute can be retrieved using this piece of jQuery code. You aways will need to adapt it. I think this will be also doable with plain JS.

```javascript
<script type="text/javascript">
        jQuery('p.add-child').click(function() {
            var prototype = $('form #id').attr('data-prototype');
            prototype = prototype.replace(/\$\$name\$\$/g, 'levelxx');
            $('#id').append(prototype);
        });
</script>
```

Closes #1497

---------------------------------------------------------------------------

by beberlei at 2011/07/18 06:40:40 -0700

form div[data-prototype]? What about forms with two collections?

---------------------------------------------------------------------------

by marcw at 2011/07/18 07:25:51 -0700

My bad, the javascript snippet is not crystal clear. Actually, you'll have to specify the div's id to retrieve the right `data-prototype` attribute.
The good way of doing this is by specifying the div's id in the selector (like in the 2nd part of the js example), like this :

    var prototype = $('form #level1_level2_level3_level3').attr('data-prototype');

---------------------------------------------------------------------------

by tystr at 2011/07/18 12:32:39 -0700

+1

---------------------------------------------------------------------------

by marcw at 2011/07/22 01:46:08 -0700

thanx @vicb ! :)
This commit is contained in:
Fabien Potencier 2011-07-22 11:04:08 +02:00
commit f29da2fcc8
9 changed files with 56 additions and 26 deletions

View File

@ -223,7 +223,7 @@ class FormExtension extends \Twig_Extension
}
}
$custom = '_'.$view->get('proto_id', $view->get('id'));
$custom = '_'.$view->get('id');
$rendering = $custom.$section;
$blocks = $this->getBlocks($view);

View File

@ -9,6 +9,15 @@
{% endspaceless %}
{% endblock form_widget %}
{% block collection_widget %}
{% spaceless %}
{% if prototype is defined %}
{% set attr = attr|merge({'data-prototype': form_row(prototype) }) %}
{% endif %}
{{ block('form_widget') }}
{% endspaceless %}
{% endblock collection_widget %}
{% block textarea_widget %}
{% spaceless %}
<textarea {{ block('widget_attributes') }}>{{ value }}</textarea>
@ -222,12 +231,6 @@
{% endspaceless %}
{% endblock field_row %}
{% block prototype_row %}
{% spaceless %}
<script type="text/html" id="{{ proto_id }}">{{ form_row(form) }}</script>
{% endspaceless %}
{% endblock prototype_row %}
{% block hidden_row %}
{{ form_widget(form) }}
{% endblock hidden_row %}

View File

@ -0,0 +1,4 @@
<?php if (isset($prototype)): ?>
<?php $attr['data-prototype'] = $view->escape($view['form']->row($prototype)) ?>
<?php endif ?>
<?php echo $view['form']->widget($form, array('attr' => $attr)) ?>

View File

@ -1,3 +0,0 @@
<script type="text/html" id="<?php echo $view->escape($proto_id) ?>">
<?php echo $view['form']->row($form) ?>
</script>

View File

@ -199,7 +199,7 @@ class FormHelper extends Helper
$template = null;
$custom = '_'.$view->get('proto_id', $view->get('id'));
$custom = '_'.$view->get('id');
$rendering = $custom.$section;
if (isset($this->varStack[$rendering])) {

View File

@ -27,7 +27,7 @@ class CollectionType extends AbstractType
{
if ($options['allow_add'] && $options['prototype']) {
$prototype = $builder->create('$$name$$', $options['type'], $options['options']);
$builder->setAttribute('prototype', $prototype);
$builder->setAttribute('prototype', $prototype->getForm());
}
$dataClass = isset($options['options']['data_class']) ? $options['options']['data_class'] : null;
@ -63,7 +63,17 @@ class CollectionType extends AbstractType
;
if ($form->hasAttribute('prototype')) {
$view->set('prototype', $form->getAttribute('prototype'));
$view->set('prototype', $form->getAttribute('prototype')->createView($view));
}
}
/**
* {@inheritdoc}
*/
public function buildViewBottomUp(FormView $view, FormInterface $form)
{
if ($form->hasAttribute('prototype') && $view->get('prototype')->get('multipart')) {
$view->set('multipart', true);
}
}

View File

@ -925,16 +925,6 @@ class Form implements \IteratorAggregate, FormInterface
$childViews[$key] = $child->createView($view);
}
if (null !== $prototype = $view->get('prototype')) {
$protoView = $prototype->getForm()->createView($view);
$protoTypes = $protoView->get('types');
$protoTypes[] = 'prototype';
$childViews[$prototype->getName()] = $protoView
->set('types', $protoTypes)
->set('proto_id', $view->get('id').'_prototype');
;
}
$view->setChildren($childViews);
foreach ($types as $type) {

View File

@ -1692,9 +1692,10 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
$html = $this->renderWidget($form);
$this->assertMatchesXpath($html,
'//script
[@id="na&me_items_prototype"]
[@type="text/html"]
'//div[@id="na&me_items"][@data-prototype]
|
//table[@id="na&me_items"][@data-prototype]
'
);
}

View File

@ -178,4 +178,29 @@ class CollectionFormTest extends TypeTestCase
$bound = $form->getData();
$this->assertInstanceOf('Symfony\Tests\Component\Form\Fixtures\Author', $bound[0]);
}
public function testGetDataDoesNotContainsProtypeNameBeforeDataAreSet()
{
$form = $this->factory->create('collection', array(), array(
'type' => 'file',
'prototype' => true,
'allow_add' => true,
));
$data = $form->getData();
$this->assertFalse(isset($data['$$name$$']));
}
public function testGetDataDoesNotContainsPrototypeNameAfterDataAreSet()
{
$form = $this->factory->create('collection', array(), array(
'type' => 'file',
'allow_add' => true,
'prototype' => true,
));
$form->setData(array('foobar.png'));
$data = $form->getData();
$this->assertFalse(isset($data['$$name$$']));
}
}