[英]Symfony - Custom FieldType form_widget rendering
长问题简短,在对此进行了大量研究并找到了有关如何扩展现有字段类型,或从它们继承,或在后端更改某些内容的一些信息之后,但对于前端的实际渲染绝对没有,我是来这里问问题。
手头“问题”的简短解释:我需要一个 EntityType 字段(ChoiceType - HTML Select)来使用我自己的过滤逻辑并从 ajax 调用中动态提取结果,立即替换下拉列表中列出的选项。
当前代码(有效):在 FormType.php 中
//in buildForm
{
$builder->add('trainer', EntityType::class, [
'class' => Trainer::class,
'choices' => $training->trainer_list ?? [],
'label' => 'seminar.trainer.form.trainer.label',
'placeholder' => 'form.trainer.placeholder',
'required' => false,
'attr' => ['class' => 'trainer2select'] // has no outcome whatsoever?!
])
$builder->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'onPreSubmit']);
}
function onPreSubmit(FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
$trainer = $this->em->getRepository(Trainer::class)->find($data['trainer']);
$form->add('trainer', EntityType::class, [
'class' => Trainer::class,
'data' => $trainer,
'label' => 'seminar.trainer.form.trainer.label',
'placeholder' => 'form.trainer.placeholder',
'required' => false,
]);
}
在树枝上:
{% if field == 'trainer' %}
{{ form_row(attribute(form, field), {'id': 'trainer'}) }}
{% else %}
{{ form_row(attribute(form, field)) }}
{% endif %}
{% block javascripts %}
<script>
var lastTime;
var timeoutEvents = [];
$(document).ready(() => {
function trainer_changed() {
let input = event.target;
lastTime = Date.now();
timeoutEvents.push(setTimeout(() => {
if (Date.now() - lastTime < 150)
return;
jQuery.ajax({
url: '{{ path('trainer_select_ajax') }}',
type: 'GET',
data: {
search: input.value,
start: {{ seminar.event.start.date | date('Y-m-d') }},
end: {{ seminar.event.end.date | date('Y-m-d') }}
},
success: function (trainers) {
let trainer = $('#trainer');
trainer.get(0).options.length = 1; // reset all options, except for the default
trainers.forEach(tr => {
trainer.append(new Option(tr.text, tr.id));
});
let search = $($("input.select2-search__field").get(1));
if (search.get(0)) {
search.get(0).oninput = null; // detach our event handler so we don't loop
search.trigger('input'); // rebuild the dropdown choices
search.get(0).oninput = trainer_changed; // reattach our event handler
}
}
});
lastTime = Date.now();
timeoutEvents.forEach(e => {
clearTimeout(e);
});
}, 200));
}
function select_opened() {
let trainerinput = $('input.select2-search__field').get(1);
if (trainerinput) {
trainerinput.oninput = trainer_changed;
}
}
$('#select2-trainer-container').click(select_opened);
});
</script>
{% endblock %}
因此,显然 EntityType 字段是使用 select2 扩展呈现的。 我显然可以用 javascript 替换该功能,但我只想定义我自己的“AjaxEntityType”,form_widget 按我的意愿呈现。 我可以在多个项目中使用的东西,而无需使用一些愚蠢的技巧,例如提供默认类名和调用 javascript 在全局页面加载后更改渲染。 那……怎么办?
我检查过的资源已经证明对我想要实现的目标几乎毫无价值: https://symfony.com/doc/current/form/form_customization.html , https://symfony.com/doc/current/form/ form_themes.html等等。
编辑澄清:我正在寻找的最佳方式是始终呈现为<select id="FieldTypeWidget">
的自定义 FieldType 的简约示例,在其上将始终调用$('#FieldTypeWidget').select2({ajax: {foo}, searchFunction: {bar}});
在https://github.com/tetranz/select2entity-bundle我可以找到如何在包中提供此功能的示例,但是在我的应用程序中是否有更简单的方法?
我会说你正在看的是 这个 Symfony 文档页面中解释的 内容
这是他们根据您的需要稍微修改的示例:
src/Form/Type/AjaxEntityType.php
<?php
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\EntityType;
class AjaxEntityType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
/**
* This is the default of your field,
* add or remove based on your needs,
* your goal, is to only keep sensible defaults
* that you want on every single objects of this class
*/
'required' => false,
]);
}
public function getParent()
{
return EntityType::class;
}
}
这是“魔法”发生的地方:
当你的类叫做WhateverNameType,Symfony的将只是删除它的类型组成部分,它正常化(简体, lcfirst
它)。
因此, WhateverNameType将以whateverName结尾。
然后,您只需要知道在渲染form_widget中调用了表单元素以最终获得正确命名的块: whateverName_widget
模板/表单/fields.html.twig
{% use 'form_div_layout.html.twig' %}
{% block ajaxEntity_widget %}
{{ parent() }}
<script>
$('#{{ form.vars.id }}').select2({ajax: {foo}, searchFunction: {bar}});
</script>
{% endblock %}
另请注意文档页面中的方便提示:
您可以进一步自定义用于呈现选择类型的每个子项的模板。 在这种情况下要覆盖的块被命名为“块名称”+条目+“元素名称”(标签、错误或小部件)(例如,要自定义运输小部件的子标签,您需要定义 {% block shipping_entry_label %} ... {% 结束块 %})。
还要记住,正如稍后在同一页面上所述,您的表单模板覆盖必须正确注册:
配置/包/twig.yaml
twig:
form_themes:
- 'form/fields.html.twig'
# you might have this configuration already,
# for example, if you use bootstrap theming.
# If so, just copy the configured template path stated here
# in the 'use' statement of the file form/fields.html.twig
然后只需使用它:
$builder->add('trainer', AjaxEntityType::class, [ class => Trainer::class, ]);
还值得一读:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.