diff --git a/assets/controllers/elements/collection_type_controller.js b/assets/controllers/elements/collection_type_controller.js index 488c6421..4f46cb0d 100644 --- a/assets/controllers/elements/collection_type_controller.js +++ b/assets/controllers/elements/collection_type_controller.js @@ -27,6 +27,7 @@ export default class extends Controller { deleteMessage: String, prototype: String, rowsToDelete: Number, //How many rows (including the current one) shall be deleted after the current row + fieldPlaceholder: String } static targets = ["target"]; @@ -65,8 +66,11 @@ export default class extends Controller { } + const regexString = this.fieldPlaceholderValue || "__name__"; + const regex = new RegExp(regexString, "g"); + //Apply the index to prototype to create our element to insert - const newElementStr = this.htmlDecode(prototype.replace(/__name__/g, this.generateUID())); + const newElementStr = this.htmlDecode(prototype.replace(regex, this.generateUID())); //Insert new html after the last child element diff --git a/src/Form/CollectionTypeExtension.php b/src/Form/CollectionTypeExtension.php index 98807f01..7ac5e56f 100644 --- a/src/Form/CollectionTypeExtension.php +++ b/src/Form/CollectionTypeExtension.php @@ -52,6 +52,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormConfigBuilder; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; @@ -87,11 +90,25 @@ class CollectionTypeExtension extends AbstractTypeExtension 'reindex_path' => 'id', ]); + //Set a unique prototype name, so that we can use nested collections + $resolver->setDefaults([ + 'prototype_name' => function (Options $options) { + return '__name_'.uniqid("", false) . '__'; + }, + ]); + $resolver->setAllowedTypes('reindex_enable', 'bool'); $resolver->setAllowedTypes('reindex_prefix', 'string'); $resolver->setAllowedTypes('reindex_path', 'string'); } + public function finishView(FormView $view, FormInterface $form, array $options) + { + parent::finishView($view, $form, $options); + //Add prototype name to view, so that we can pass it to the stimulus controller + $view->vars['prototype_name'] = $options['prototype_name']; + } + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($options): void { diff --git a/templates/components/collection_type.macro.html.twig b/templates/components/collection_type.macro.html.twig index 5fcc8053..1db04763 100644 --- a/templates/components/collection_type.macro.html.twig +++ b/templates/components/collection_type.macro.html.twig @@ -4,11 +4,13 @@ 'deleteMessage': deleteMessage|trans, 'prototype': form_widget(form.vars.prototype)|e('html_attr'), 'rowsToDelete': rowsToDelete, + 'fieldPlaceholder': form.vars.prototype_name }) }} {% else %} {# If add_element is disabled/forbidden, prototype is not available #} {{ stimulus_controller('elements/collection_type', { 'deleteMessage': deleteMessage|trans, - 'rowsToDelete': rowsToDelete + 'rowsToDelete': rowsToDelete, + 'fieldPlaceholder': form.vars.prototype_name }) }} {% endif %} {% endmacro %}