Symfony: How to dynamically add Lines to a Poll?

爷,独闯天下 提交于 2020-12-15 04:57:05

问题


I created a Symfony Poll-Bundle which has the Entitys Campaign->Block->Line->Field->PollResult. So i have a CollectionType CampaignType which consists of many blocks. Block is also a CollectionType and consists of many Lines. One Line consist of many Fields and every Field has one PollResult which holds the Answer of the user who filled out the campaign. So the Campaign could be a Covid-19 Campaign with a block in it called Symptoms, which has Lines for the different Symptoms. Each Line has a dropdown with the Kind of Symptom (like Fever or Headache) the intensity, and two dates for the period when the Symptom appeared.

So i added 'allow_add' => true to my BlockType:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('lines', CollectionType::class, [
        'entry_type' => LineType::class,
        'entry_options' => ['label' => false],
        'allow_add' => true,
        'by_reference' => false,
    ]);
}

Then there are LineType, which add fields:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('fields', CollectionType::class, array(
        'entry_type' => FieldType::class,
        'entry_options' => array('label' => false),
    ));
}

and FieldType to add the pollResults:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('pollResults', CollectionType::class, array(
        'entry_type' => PollResultType::class,
        'entry_options' => array('label' => false),
    ));
}

and the pollResultType is a little more complex, because there are many types of input fields so i will put it at the end of this question!

On my html-page i have some for loops to display my form (i only show relevant stuff):

{{ form_start(form, {'attr': {'id': 'campaignEdit_form'} }) }}
{% for block in form.blocks %}
    {% for line in block.lines %}
        <ul class="singleLineUL lst-none" data-prototype="{{ form_widget(form.blocks[loop.parent.loop.index0].lines.vars.prototype)|e('html_attr') }}">
            <li>
                {% for field in line.fields %}
                    <div class=" {% if loop.first %} lineFieldFirst {% else %} lineField {% endif %} ">
                        {% for pollResult in field.pollResults %}
                            <div class="formLabelDiv">{{ field.vars.value.getTranslationName(app.request.getLocale()) }}</div>
                            <div class="formWidgetDiv">{{ form_widget(pollResult) }}</div>
                        {% endfor %}
                    </div>
                {% endfor %}
            </li>
        </ul>
    {% endfor %}
{% endfor %}

So in the ul.singleLineUL element i place the data-prototype which should hold the prototype for the four fields i described above (SymptomType, Intensity, and two date fields) Here is the Javascript code which adds the 'Add a Line'-Button and should add a Line when i press this Button:

<script>
    var $collectionHolder;
    //setup an "add a Line" Link
    var $addLineButton = $('<button type="button" class="add_line_link">Add a Line</button>');
    var $newLinkLi = $('<li></li>').append($addLineButton);

    jQuery(document).ready(function (){

        //Get the ul that holds the collection of fields
        $collectionHolder = $('ul.singleLineUL')

        // add the "add a line" anchor and li to the tags ul
        $collectionHolder.append($newLinkLi);

        // count the current form inputs we have (e.g. 2), use that as the new
        // index when inserting a new item (e.g. 2)
        $collectionHolder.attr('data-index', $collectionHolder.find(':input').length);

        $addLineButton.on('click', function(e) {
            // add a new tag form (see next code block)
            addFieldsForm($collectionHolder, $newLinkLi);
        });

    })

    function addFieldsForm($collectionHolder, $newLinkLi){
        //get the data-prototyp

        console.log($collectionHolder);

        var prototype = $collectionHolder.attr('data-prototype');
        console.log(prototype);

        //get the new index
        var index = $collectionHolder.attr('data-index');

        var newForm = prototype;
        console.log(newForm);

        newForm = newForm.replace(/__name__/g, index);
        console.log(newForm);

        $collectionHolder.attr('data-index', index+1);

        // Display the form in the page in an li, before the "Add a tag" link li
        var $newFormLi = $('<li></li>').append(newForm);
        $newLinkLi.before($newFormLi);
    }
</script>

But the only thing that happens is that a Label with the text 'Fields' gets printed and the prototype obviously only holds that label.

How can i make this Button to duplicate this whole Line and adds four new Fields?

-

I don't think that this will help a lot but here is also the pollResultType Class:

class PollResultType extends AbstractType
{
    protected $requestStack;

    public function __construct(RequestStack $requestStack)
    {
        $this->requestStack = $requestStack;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $request = $this->requestStack->getCurrentRequest();
        $locale = $request->getLocale();

        $formFactory = $builder->getFormFactory();
        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formFactory, $locale) {
            /** @var PollResult $data */
            $data = $event->getData();
            $form = $event->getForm();

            $fieldOptions = [];
            $fieldOptions['auto_initialize'] = false;
            $fieldOptions['label'] = false;

            switch ($data->getField()->getType()){
                case 'choice':
                    /** @var Fieldvalue $fieldvalue */
                    foreach ($data->getField()->getFieldvalues() as $fieldvalue) {
                        $fieldOptions['choices'][$fieldvalue->getValue()] = $fieldvalue->getTranslationName($locale);
                    }
                    break;
                case 'radio':
                case 'gi_sex':
                    $fieldOptions['multiple'] = false;
                    $fieldOptions['expanded'] = true;
                    /** @var Fieldvalue $fieldvalue */
                    foreach ($data->getField()->getFieldvalues() as $fieldvalue) {
                        $fieldOptions['choices'][$fieldvalue->getValue()] = $fieldvalue->getTranslationName($locale);
                    }
                    break;
                case 'checkbox':
                case 'text':
                case 'gi_smallTown':
                    $fieldOptions['required'] = false;
                    break;
                case 'date':
                    $fieldOptions['required'] = false;
                    $fieldOptions['attr'] = ['class' => 'datepicker'];
                    break;
                case 'gi_country':
                    $fieldOptions['attr'] = ['id' => 'chooseCountry', 'data-toogle' => 'country', 'name'=> 'country', 'onchange' => 'changeDataCountry()'];
                    break;
                case 'gi_state':
                    $fieldOptions['attr'] = ['id' => 'chooseState', 'data-toggle' => 'state', 'name'=> 'state'];
                    break;
            }

            switch ($data->getField()->getType()){
                case 'radio':
                case 'gi_sex':
                case 'gi_country':
                case 'gi_state':
                    $form->add($formFactory->createNamed('value', 'choice', null, $fieldOptions));
                    break;
                case 'date':
                    $form->add($formFactory->createNamed('value', "text", null, $fieldOptions));
                    break;
                case 'gi_createdate':
                case 'gi_editdate':
                    $form->add($formFactory->createNamed('value', "hidden", null, $fieldOptions));
                    break;
                case 'gi_smallTown':
                    $form->add($formFactory->createNamed('value', "checkbox", null, $fieldOptions));
                    break;
                case 'gi_age':
                    $form->add($formFactory->createNamed('value', "number", null, $fieldOptions));
                    break;
                default:
                    $form->add($formFactory->createNamed('value', $data->getField()->getType(), null, $fieldOptions));
                    break;
            }
        });

        $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($formFactory) {
            $data = $event->getData();
            $fieldOptions = [];
            $fieldOptions['choices'][$data["value"]] = $data["value"];
            $fieldOptions['auto_initialize'] = false;

            $event->getForm()->add($formFactory->createNamed('value', "choice", null, $fieldOptions));
        });
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => PollResult::class,
        ));
    }
}

来源:https://stackoverflow.com/questions/65092053/symfony-how-to-dynamically-add-lines-to-a-poll

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!