如何从symfony 4表单中的嵌入式集合中获取添加的项目数据?allow_add似乎不起作用



我已经完成了一个表单,其中嵌入了一个实体(Crop Production)及其子项(事件),类似于我们在这里可以找到的表单,即jQuery动态添加/删除项。

除了表单中添加的项目没有保存在请求中之外,子实体的编辑/删除一切正常。

当我用jQuery在模板中添加新项目时,表单原型/标签似乎没有被识别/映射/链接到表单,但我不明白为什么原型是由symfony自动生成的,即动态添加的输入的名称是好的。

edit.html.twig(带有jQuery的模板)

{% extends picture is not defined ? 'base.html.twig' : 'blank.html.twig' %}


{% block main %}
<div class="card card-body">
{{ form_start(form) }}
{{ form_row(form.Crop_production) }}
{# render each row and field in the embedded form, in this example it's rendering the experience (Exp) fiels as rows #}
</div>
<div class="col-sm-12 col-sm-offset-2">
<legend class="col-form-label required mt-3">Crop production events</legend>
<div class="row" id="event_list" data-prototype="{{ form_widget(form.cropProductionEvents.vars.prototype)|e }}">
{% for row in form.cropProductionEvents %}
<div class="panel panel-warning">
<div class="col-md-12 col-xl-12">
<div class="card mb-3 mt-3 widget-content">
<div class="widget-content-outer">
{{ form_row(row) }}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{{ form_end(form) }}
<br>

<script>
// this variable is the list in the dom, it's initiliazed when the document is ready
console.log("init");
var $collectionHolder;
// the link which we click on to add new items
var $addNewItem = $('<a href="#" class="btn btn-info mb-3 ml-3">Add new event</a>');
// when the page is loaded and ready
$(document).ready(function () {
console.log("ready");
// get the collectionHolder, initilize the var by getting the list;
$collectionHolder = $('#event_list');
// append the add new item link to the collectionHolder
$collectionHolder.parent().append($addNewItem);
// add an index property to the collectionHolder which helps track the count of forms we have in the list
$collectionHolder.data('index', $collectionHolder.find('.panel').length)
// finds all the panels in the list and foreach one of them we add a remove button to it
// add remove button to existing items
$collectionHolder.find('.panel').each(function () {
// $(this) means the current panel that we are at
// which means we pass the panel to the addRemoveButton function
// inside the function we create a footer and remove link and append them to the panel
// more informations in the function inside
addRemoveButton($(this));
});
// handle the click event for addNewItem
$addNewItem.click(function (e) {
// preventDefault() is your  homework if you don't know what it is
// also look up preventPropagation both are usefull
e.preventDefault();
// create a new form and append it to the collectionHolder
// and by form we mean a new panel which contains the form
addNewForm();
})
});
/*
* creates a new form and appends it to the collectionHolder
*/
function addNewForm() {
console.log("add new form");
// getting the prototype
// the prototype is the form itself, plain html
var prototype = $collectionHolder.data('prototype');
// get the index
// this is the index we set when the document was ready, look above for more info
var index = $collectionHolder.data('index');
// create the form
var newForm = prototype;
// replace the __name__ string in the html using a regular expression with the index value
newForm = newForm.replace(/__name__/g, index);
// incrementing the index data and setting it again to the collectionHolder
$collectionHolder.data('index', index+1);
// create the panel
// this is the panel that will be appending to the collectionHolder
var $panel = $('<div class="panel panel-warning"></div>');
// create the panel-body and append the form to it
var $panelBody = $('<div class="col-md-12 col-xl-12"></div>').append($('<div class="card mb-3 mt-3 widget-content"></div>').append($('<div class="widget-content-outer"></div>').append($('<fieldset class="form-group"></fieldset>').append(newForm))));
// append the body to the panel
$panel.append($panelBody);
// append the removebutton to the new panel
addRemoveButton($panel);
// append the panel to the addNewItem
// we are doing it this way to that the link is always at the bottom of the collectionHolder
$panel.hide();
$("#event_list").append($panel);
$panel.slideDown(200);
console.log("selection holder");
console.log($collectionHolder.data('index'));
}
/**
* adds a remove button to the panel that is passed in the parameter
* @param $panel
*/
function addRemoveButton ($panel) {
console.log("add remove button");
// create remove button
var $removeButton = $('<a href="#" class="btn btn-danger">Remove</a>');
// appending the removebutton to the panel footer
var $panelFooter = $('<div class="panel-footer"></div>').append($removeButton);
// handle the click event of the remove button
$removeButton.click(function (e) {
e.preventDefault();
// gets the parent of the button that we clicked on "the panel" and animates it
// after the animation is done the element (the panel) is removed from the html
$(e.target).parents('.panel').slideUp(200, function () {
$(this).remove();
})
});
// append the footer to the panel
var $form = $panel.find(".widget-content-outer");
console.log($form);
$form.append($panelFooter);
}
</script>
{% endblock %}

CropProductionType.php(父窗体类型)

<?php
namespace AppForm;
use AppEntityCropProduction;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
use SymfonyBridgeDoctrineFormTypeEntityType;
use SymfonyComponentFormExtensionCoreType as Type;
class CropProductionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$units = [
"money" => [
'currency' => 'USD',
'help' => 'value in $',
'required' => false,
"attr" => ["step" => '0.01']
],
"surface" => [
'help' => 'value in ha',
"attr" => ["step" => '0.01']
]
];
$builder->add(
$builder->create('Crop_production', TypeFormType::class, [
'inherit_data' => true,
'row_attr' => [
'class' => 'form-col-2'
],
])
->add('type', TypeChoiceType::class, ["label" => "Type harvested", "required" => true, "choices" => [
"Grain" => "Grain",
"Fruit" => "Fruit",
"Tuber" => "Tuber",
"Stem" => "Stem"
]
])
->add('dateHarvested', TypeDateType::class, [
'label' => 'Date Harvested',
'widget' => 'single_text',
// this is actually the default format for single_text
'format' => 'yyyy-MM-dd',
])
->add('prodFarmerStatement', TypeNumberType::class, ["label" => "Production farmer statement", 'help' => 'in kg dry/ha (grain) or in ton fresh/ha (other)'])
->add('prodFieldMeasure', TypeNumberType::class, ["label" => "Production field measure", 'help' => 'in kg dry/ha (grain) or in ton fresh/ha (other)'])
);

$builder->add('cropProductionEvents', TypeCollectionType::class, [
'entry_type' => CropProductionEventType::class,
'entry_options' => [
'label' => false
],
// this allows the creation of new forms and the prototype too
'allow_add' => true,
// self explanatory, this one allows the form to be removed
'allow_delete' => true,
'delete_empty' => true,
'prototype' => true,
'by_reference' => false,
]);


$builder->add('save', TypeSubmitType::class, ['label' => 'Save']);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => CropProduction::class,
'label' => 'fdg',
]);
}

}

CropProductionController.php编辑函数(父控制器)

/**
* @Route("{id}/delete", name="_delete")
*/
public function delete_production($id)
{
$em = $this->getDoctrine()->getManager();
$crop_production = $em->getRepository(CropProduction::class)->find($id);
$crop = $crop_production->getCrop();
$em->remove($crop_production);
$em->flush();
return $this->redirectToRoute("crop_details", ["id" => $crop->getId(), '_fragment' => 'crop_production']);
}
/**
* @Route("{id}/edit", name="_edit")
*/
public function edit(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$form = NULL;
$crop_production = $em->getRepository(CropProduction::class)->find($id);
// $events = new ArrayCollection();
// foreach ($crop_production->getCropProductionEvents() as $event) {
//   $events->add($event);
// }
if(!$crop_production) return $this->render('error/blank.html.twig', []);
$form = $this->createForm(CropProductionType::class, $crop_production);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {

$em->persist($crop_production);
$em->flush();
return $this->redirectToRoute("crop_details", ["id" => $crop_production->getCrop()->getId()]);
}

return $this->render('crop_production/edit.html.twig', [
"crop_prod" => $crop_production,
'form' => $form->createView(),
]);
}

你知道我可能会错过什么来在模板上添加元素吗?谢谢

您的<div class="row" id="event_list" >上没有data-index=""。你必须把当前的孩子数量(收集的类型),这样addNewForm()就可以指望…

像这样:

<div class="row" id="event_list" data-index="{{ form.cropProductionEvents|length }}" data-prototype="{{ form_widget(form.cropProductionEvents.vars.prototype)|e }}">

相关内容

最新更新