Предотвратить InvalidArgumentException при создании поля формы Dynami c - PullRequest
0 голосов
/ 16 июня 2020

Я следую Symfony cookbook при создании полей формы c.

В основном, в моем случае у меня есть Product, ProductVersion и поле Quantity в моей форме. В новых формах ProductVersion скрыт (только с атрибутом класса, это все еще EntityType).

При изменении Product (через меню выбора) я делаю запрос AJAX, чтобы увидеть если для этого товара есть ProductVersion. Если это так, я заполняю ProductVersion доступными версиями и показываю их пользователю.

Он отлично работает с новыми формами. Но при редактировании той же формы у меня есть ответ InvalidArgumentException на мой AJAX запрос, который сообщает мне, что поле Quantity равно null:

Expected argument of type "int", "null" given at property path "quantity".

Я понимаю, что действительно, я не Не предоставляю quantity при отправке моей формы через запрос AJAX, но цель этого метода, не так ли? Чтобы отправить только поле, которое меняет динамическое c поле.

Как я могу сделать, чтобы избежать этого исключения?

Вот ItemType:

public function buildForm(FormBuilderInterface $builder, array $options)
    {

        $builder
            ->add('product',  EntityType::class, [
                'label'  => 'item.product',
                'class' => Product::class,
                'placeholder' => 'item.product',
            ])
        ;

        $formModifier = function (FormInterface $form, Product $product = null) {
            if(null !== $product){
                $productVersions = $product->getVersions();
                if(count($productVersions) > 0){
                    $form->add('productVersion', EntityType::class, [
                        'class' => 'App\Entity\ProductVersion',
                        'placeholder' => 'item.product_version',
                        'choices' => $productVersions,
                        'label' => 'item.product_version'
                    ]);
                } 
                else{
                    $form->add('productVersion', EntityType::class, [
                        'class' => 'App\Entity\ProductVersion',
                        'placeholder' => 'item.product_version',
                        'choices' => [],
                        'label' => 'item.product_version',
                        'disabled' => true,
                        'row_attr' => [
                            //'class' => 'd-none'
                        ]
                    ]);
                }
            } 
            else{
                $form->add('productVersion', EntityType::class, [
                    'class' => 'App\Entity\ProductVersion',
                    'placeholder' => 'item.product_version',
                    'choices' => [],
                    'label' => 'item.product_version',
                    'disabled' => true,
                    'row_attr' => [
                        //'class' => 'd-none'
                    ]
                ]);
            }
        };


        $builder->addEventListener(
            FormEvents::PRE_SET_DATA, 
            function (FormEvent $event) use ($formModifier) {
                $form = $event->getForm();
                $data = $event->getData();
                $options = $event->getForm()->getConfig()->getOptions();

                //add custom product version according product selected
                $formModifier($event->getForm(), $data->getProduct());

                $form
                    ->add('quantity', IntegerType::class, [
                        'label' => 'item.quantity',

                    ])
                ;
                if ($data->getId()) {
                    $form
                        ->add('save', SubmitType::class, [
                            'label'  => $options['submit_btn_label'],
                        ])
                    ;
                } else {
                    $form
                        ->add('save', SubmitType::class, [
                            'label'  => $options['submit_btn_label'],
                        ])
                    ;
                }
            }
        );

        $builder->get('product')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formModifier) {
                // It's important here to fetch $event->getForm()->getData(), as
                // $event->getData() will get you the client data (that is, the ID)
                $product = $event->getForm()->getData();
                // since we've added the listener to the child, we'll have to pass on
                // the parent to the callback functions!
                $formModifier($event->getForm()->getParent(), $product);
            }
        );

    }

А вот часть JS:

$(document).ready( function () {
    var $product = $('#item_product');
    // When product gets selected ...
    $product.on('change', function() {
        console.log("product has changed")
        // ... retrieve the corresponding form.
        var $form = $(this).closest('form');
        // Simulate form data, but only include the selected product value.
        var data = {};
        data[$product.attr('name')] = $product.val();

        // Submit data via AJAX to the form's action path.
        console.log(data);
        $.ajax({
            url : $form.attr('action'),
            type: $form.attr('method'),
            data : data,
            success: function(html) {
                // Replace current position field ...
                $('#item_productVersion').closest('.form-group').replaceWith(
                    // ... with the returned one from the AJAX response.
                    $(html).find('#item_productVersion').closest('.form-group')
                );
                // Position field now displays the appropriate positions.
            }
        });
    });
})

1 Ответ

0 голосов
/ 18 июня 2020

Как предложил @Jakumi, добавление опции empty_data в поле quantity решило проблему. Тем не менее, он выдает ошибку в quantity в форме, полученной через запрос AJAX, и это нормально.

Я обнаружил, что это не «чистый» метод (вам нужно настроить поля формы, вы загружаете всю страницу при каждом запросе AJAX, et c ..), поэтому я решил создать специальный маршрут, посвященный отправке форм, которые предназначены для добавления / удаления / редактирования полей.

Этот маршрут создает форму new с предустановленными полями (в моем случае product выбирается пользователем). Затем я могу заполнить другое поле (в моем случае «ProductVersion») обычным событием формы PRE_SET_DATA.

Таким образом:
- мне не нужно беспокоиться о каком-либо другом обязательном поле
- я могу сериализовать всю форму в моем AJAX запросе (не нужно искать поле, все выполняется в конструкторе форм)
- Запрос AJAX выводит только форму (я думаю, это немного улучшает производительность)

Конструктор форм не меняется (вам все равно нужно слушать PRE_SET_DATA и POST_SUBMIT. Если вы не слушаете POST_SUBMIT, форма не узнает о choices, которое вы добавили динамически, и выдаст вам ошибку).

Вот часть JS:

$product.on('change', function() {
        // ... retrieve the corresponding form.
        var $form = $(this).closest('form');
        // serialize the entire form
        var data = $form.serializeArray();

        // Submit data via AJAX to the form's action path.
        console.log(data);
        $.ajax({
            url : '/project/item/partial-edit', //custom route
            type: $form.attr('method'),
            data : data,
            success: function(html) {
                // Replace current position field ...
                $('#item_productVersion').closest('.form-group').replaceWith(
                    // ... with the returned one from the AJAX response.
                    $(html).find('#item_productVersion').closest('.form-group')
                );
            }
        });
    });

Вот специальный маршрут:

    /**
     * @Route("/item/partial-edit", name="edit_item_partial", requirements={"project"="\d+","item"="\d+"})
     */
    public function edit_item_partial(Request $request)
    {

        $item = new Item();
        $formOption = $request->get('option') ?? array(
            'submit_btn_label' => 'update'
        );
        $form = $this->createForm(ItemType::class, $item, $formOption);
        $form->handleRequest($request);
        if ($form->isSubmitted()){
            //if form was submitted, create a new form with the $item that has now a Product given
            $newForm = $this->createForm(ItemType::class, $item, $formOption);

            //here is the custom view only rendering the form
            return $this->render('item/new_form-only.html.twig', [ 
                'form' => $newForm->createView(),
            ]);

        }
        else {
            return new Response('No form was submitted');
        }

    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...