创建 'Ajaxified' 表单字段类型

Creating a 'Ajaxified' Form Field Type

在我的应用程序中,我有几个带有许多选项的表单域。我遇到的问题类似于 this question:在每次页面加载时获取和解析所有选项的成本很高(Twig 一遍又一遍地呈现所有选项,而客户端不可能缓存)。这个问题让我创造了一种通过 AJAX 将选项发送到浏览器的方法。相当简单的方法:

  1. 通过AJAX获取所有选项(键值)(例如通过获取/countries.json)并尽可能缓存。 (在这种情况下,国家名称不太可能经常更改)
  2. 使用 selectize、select2 或类似插件将选项插入 DOM。
  3. 享受更快的表单:-)

为了防止 Symfony 查询所有选项(没有必要:它们通过 AJAX 加载)我在加载表单时将 setMaxResults(0) 添加到 QueryBuilder(通过添加通过控制器选择)。是的,那是kludge。提交表单时它仍会执行查询,因为它必须验证所选选项是否存在(并检查约束)。

我想创建一个 custom Form Field Type that adds this functionality to the current EntityType: don't load the options while rendering the form, but still check if the selected option exists. I found many examples related to dynamically modifying a form,但我还没有找到与只修改一个表单字段相关的示例,而与它的父表单无关。

如何创建这样的表单字段类型?什么是好的起点?扩展 EntityTypeChoiceType 或其他方法?

我已经在使用 Symfony 3.1,所以使用 lazy loading of form choices(Symfony 3.2 中的新功能)不会有问题。不确定这个新功能是否与我的问题有关。

考虑到您的用例,最好的方法是使用自动完成。这是我一直在使用的 Symfony bundle,它很棒。根据我的情况,我做了一些覆盖它的javascript(自动完成器)以增强一些功能。

带有 Ajax 控制器选项的自动完成对我来说很不错,但这是另一个(可能更快?)选项:通过 hiinclude 呈现您的表单。

hinclude is a JS library used to "defer" load of parts of a page, thought Ajax. Symfony comes with integrated support (official documentation).

如何使用它:

  • 将您的表单渲染移动到另一个控制器操作,我们称之为 formAction
  • 在您的页面上包含 hinclude.js(参见官方 Github)
  • 使用此代码呈现您的表单:

    {{ render_hinclude(控制器('...::form'), {'default': 'Loading...'}) }}

  • 您可能希望在原始控制器操作中保留表单处理,因此修改生成表单的 "action" 如下:

    $form = $this->createForm(new FormType(), $obj, array( 'action' => $this->generateUrl('original_form_action')));

我写了一个包 (Alsatian/FormBundle),它在服务器端做你想做的事。

  • How to avoid loading each entities by each form rendering :

    abstract class AbstractExtensibleChoicesType extends AbstractRoutableType
    {
        public function configureOptions(OptionsResolver $resolver)
        {  
            $resolver->setDefault('choices',array());
        }
    }
    
  • 如何使用缓存的内容填充表单字段:

那是你自己的逻辑,我建议:创建一个只有 returns(如 HTML)的控制器:

<option value="1">Option 1</option>
<option value="2">Option 2</option>

在控制器中设置 Maxage :

/*
* @Route(...)
* @Cache(maxage=64000)
*/
public function getOptionsAction(Request $request) // Home
{
    $choices = $this->getDoctrine()->getManager()->getRepository //....

    return $this->render(/*...*/);
}

使用 javascript 加载此 url 并将 html 结果放入您的 select 字段。

如果您使用类似 Select2 的东西: 您的控制器还可以 return 作为 JSONReponse() 的选项,然后您可以直接从 select2 ajax 选项加载此 JSON(参见包文档,这就是我使用它的方式)。

在 Form::PRE_SUBMIT 事件中获取提交的选项(如果您使用表单进行编辑,也 PRE_SET_DATA),并将这些选项重新注入字段。