diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
index 7f5e323975478..c647a3d5436b2 100644
--- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
+++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
@@ -96,7 +96,7 @@
{{ block('choice_widget_options') }}
{% else %}
-
+
{% endif %}
{% endfor %}
{% endspaceless %}
@@ -423,3 +423,16 @@
{%- endfor -%}
{% endspaceless %}
{% endblock button_attributes %}
+
+{% block option_data_attributes %}
+{% spaceless %}
+ {%- for attrname, attrvalue in choice.attr -%}
+ {{- " " -}}
+ data-{{attrname}}="{{attrvalue}}"
+ {%- endfor -%}
+
+ {% if choice.prototype %}
+ data-prototype="{{ form_row(choice.prototype)|e }}"
+ {% endif %}
+{% endspaceless %}
+{% endblock option_data_attributes %}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_options.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_options.html.php
index a7a9311d51326..1f518fabbfd74 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_options.html.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_options.html.php
@@ -6,6 +6,6 @@
block($form, 'choice_widget_options', array('choices' => $choice)) ?>
-
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/option_data_attributes.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/option_data_attributes.html.php
new file mode 100644
index 0000000000000..51310db0e2e03
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/option_data_attributes.html.php
@@ -0,0 +1,6 @@
+attr as $k => $v): ?>
+data-=""
+
+prototype): ?>
+ data-prototype="escape($formHelper->block($choice->prototype, 'form_row')) ?>"
+
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
index cdaee86b25b03..9010d74492257 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
@@ -17,6 +17,7 @@
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Exception\LogicException;
+use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList;
use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener;
use Symfony\Component\Form\Extension\Core\EventListener\FixCheckboxInputListener;
@@ -26,6 +27,7 @@
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToBooleanArrayTransformer;
use Symfony\Component\OptionsResolver\Options;
+use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ChoiceType extends AbstractType
@@ -36,6 +38,11 @@ class ChoiceType extends AbstractType
*/
private $choiceListCache = array();
+ /**
+ * @var array
+ */
+ private $choicesPrototypeForm = array();
+
/**
* {@inheritdoc}
*/
@@ -88,6 +95,23 @@ public function buildForm(FormBuilderInterface $builder, array $options)
// transformation is merged back into the original collection
$builder->addEventSubscriber(new MergeCollectionListener(true, true));
}
+
+ if ($options['choices_prototypes']) {
+ foreach ($options['choices_prototypes'] as $prototype) {
+ $resoler = new OptionsResolver();
+ $resoler
+ ->setDefaults(array('options' => array()))
+ ->setRequired(array('name', 'type'))
+ ->setAllowedTypes(array(
+ 'name' => array('string'),
+ 'type' => array('string', 'Symfony\Component\Form\FormTypeInterface'),
+ 'options' => array('array'),
+ ))
+ ;
+ $prototype = $resoler->resolve($prototype);
+ $this->choicesPrototypeForm[] = $builder->create($prototype['name'], $prototype['type'], $prototype['options'])->getForm();
+ }
+ }
}
/**
@@ -132,6 +156,18 @@ public function buildView(FormView $view, FormInterface $form, array $options)
// POST request.
$view->vars['full_name'] = $view->vars['full_name'].'[]';
}
+
+ if (!empty($options['choices_attributes']) || !empty($options['choices_prototypes'])) {
+ foreach ($view->vars['choices'] as $key => $choiceView) {
+ if (array_key_exists($key, $options['choices_attributes'])) {
+ $choiceView->attr = $options['choices_attributes'][$key];
+ }
+
+ if (array_key_exists($key, $this->choicesPrototypeForm) && $this->choicesPrototypeForm[$key] instanceof FormInterface) {
+ $choiceView->prototype = $this->choicesPrototypeForm[$key]->createView($options['choices_prototypes_view']($view));
+ }
+ }
+ }
}
/**
@@ -203,6 +239,14 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
return $emptyValue;
};
+ $choicesClosureNormalizer = function (Options $options, $values) {
+ if ($values instanceof \Closure && !is_array($values = $values($options['choice_list']))) {
+ throw new UnexpectedTypeException($values, 'array');
+ }
+
+ return $values;
+ };
+
$compound = function (Options $options) {
return $options['expanded'];
};
@@ -221,14 +265,25 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
// is manually set to an object.
// See https://github.com/symfony/symfony/pull/5582
'data_class' => null,
+ // All options related to choices extra data
+ 'choices_attributes' => array(),
+ 'choices_prototypes' => array(),
+ 'choices_prototypes_view' => function ($view) {
+ return $view->parent;
+ }
));
$resolver->setNormalizers(array(
'empty_value' => $emptyValueNormalizer,
+ 'choices_attributes' => $choicesClosureNormalizer,
+ 'choices_prototypes' => $choicesClosureNormalizer
));
$resolver->setAllowedTypes(array(
- 'choice_list' => array('null', 'Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface'),
+ 'choice_list' => array('null', 'Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface'),
+ 'choices_attributes' => array('array', '\Closure'),
+ 'choices_prototypes' => array('array', '\Closure'),
+ 'choices_prototypes_view' => array('\Closure'),
));
}
diff --git a/src/Symfony/Component/Form/Extension/Core/View/ChoiceView.php b/src/Symfony/Component/Form/Extension/Core/View/ChoiceView.php
index 97cdd214c28f8..e58d45b233893 100644
--- a/src/Symfony/Component/Form/Extension/Core/View/ChoiceView.php
+++ b/src/Symfony/Component/Form/Extension/Core/View/ChoiceView.php
@@ -18,6 +18,20 @@
*/
class ChoiceView
{
+ /**
+ * Attributes which can be used in view.
+ *
+ * @var array
+ */
+ public $attr = array();
+
+ /**
+ * A prototype who can be used in view.
+ *
+ * @var FormView
+ */
+ public $prototype;
+
/**
* The original choice value.
*