【问题标题】:Symfony - Custom FieldType form_widget renderingSymfony - 自定义 FieldType form_widget 渲染
【发布时间】:2019-03-12 20:19:53
【问题描述】:

简短的问题,在对此进行了大量研究并找到了一些关于如何扩展现有字段类型,或从它们继承,或在后端更改某些内容的信息之后,但对于前端的实际渲染绝对没有,我是来问这个问题的。

对手头的“问题”的简短解释: 我需要一个 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.htmlhttps://symfony.com/doc/current/form/form_themes.html 等等。

为澄清而编辑:我正在寻找的最佳情况是自定义 FieldType 始终呈现为&lt;select id="FieldTypeWidget"&gt; 的简约示例,将始终在其上调用$('#FieldTypeWidget').select2({ajax: {foo}, searchFunction: {bar}});

https://github.com/tetranz/select2entity-bundle 我可以找到一个如何在捆绑包中提供此功能的示例,但是在我的应用程序中是否有更简单的方法?

【问题讨论】:

  • 正如您在我的代码中看到的那样,我已经在使用表单事件将正确的实体放入后端的表单中。我想要的是定义一个自定义的 EntityType,无论在哪里使用,它都会按照我定义的方式呈现(在哪里以及如何?) - 到目前为止,我什至无法真正找到 symfony 的 form_widget 如何决定呈现它自己的类型

标签: javascript php symfony twig widget


【解决方案1】:

我会说你正在看的是this Symfony documentation page中解释的内容

这是他们的示例,根据您的需要进行了一些修改:

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 只会删除它的 Type 部分并对其进行规范化(简而言之,lcfirst it)。
所以 WhateverNameType 将以 whateverName 结尾。
然后,您只需要知道在渲染 form_widget 中调用了表单元素,最终得到正确命名的块:whateverName_widget

模板/form/fields.html.twig

{% use 'form_div_layout.html.twig' %}

{% block ajaxEntity_widget %}
    {{ parent() }}
    <script>
        $('#{{ form.vars.id }}').select2({ajax: {foo}, searchFunction: {bar}});
    </script>
{% endblock %}

还请注意文档页面中的方便提示:

您可以进一步自定义用于渲染每个子级的模板 选择类型。在这种情况下要覆盖的块被命名为“块 name" + entry + "element name"(标签、错误或小部件)(例如 自定义您需要的 Shipping 小部件的子级标签 定义 {% block shipping_entry_label %} ... {% endblock %})。

还请记住,正如稍后在同一页面上所指出的,您的表单模板覆盖必须正确注册:

config/packages/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, ]);

值得一读:

【讨论】:

  • 该死的,我在浏览文档时不知何故跳过了那部分,因为 twig 似乎不是正确的查看位置。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-29
  • 2012-12-28
  • 2022-01-10
  • 2013-07-27
  • 1970-01-01
相关资源
最近更新 更多