Я следую 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.
}
});
});
})