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. *