本文介绍了Symfony 2.8 动态 ChoiceType 选项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的项目中,我有一些带有很多选项的选择类型的表单.

in my project I have some forms with choice types with a lot of options.

所以我决定构建一个基于 jquery 自动完成的自动完成选择类型,它在运行时将新的 HTML 元素添加到原始 中.选择后,它们会正确提交,但无法在默认的 ChoicesToValuesTransformer 中处理,因为在我创建表单时,它不存在于我的表单中.

So I decided to build an autocomplete choice type based on jquery autocomplete, which adds new <option> HTML elements to the original <select> on runtime. When selected they are submitted correctly, but can't be handled within the default ChoicesToValuesTransformer, since the don't exist in my form when I create it.

如何让 symfony 接受我动态添加的值?

How can I make symfony accept my dynamically added values?

我找到了这个答案 在 Symfony 2 中验证动态加载的选择 ,其中提交的值用于修改 PRE_SUBMIT 表单事件上的表单,但无法在我的情况下运行它.我需要更改当前类型已知的选项,而不是向表单添加新的小部件

I found this answer Validating dynamically loaded choices in Symfony 2 , where the submitted values are used to modify the form on the PRE_SUBMIT form event, but couldn't get it running for my situation. I need to change choices known to the current type instead of adding a new widget to the form

推荐答案

要处理动态添加的值,请使用选择类型的 'choice_loader' 选项.它是 symfony 2.7 中的新功能,遗憾的是没有'根本没有任何文档.

To deal with dynamically added values use 'choice_loader' option of choice type. It's new in symfony 2.7 and sadly doesn't have any documentaion at all.

基本上它是一个实现 ChoiceLoaderInterface 的服务,它定义了三个函数:

Basically it's a service implementing ChoiceLoaderInterface which defines three functions:

  • loadValuesForChoices(array $choices, $value = null)
    • 在构建表单上调用并接收绑定到表单中的对象的预设值
    • 在构建视图上调用,并应返回一般的完整选择列表
    • 在表单提交时调用并接收提交的数据

    现在的想法是在选择加载器中保留一个 ArrayChoiceList 作为私有属性.在构建表单 loadValuesForChoices(...) 被调用时,我们将所有预设选项添加到我们的选择列表中,以便向用户显示它们.在构建视图 loadChoiceList(...) 被调用,但我们不加载任何东西,我们只是返回我们之前创建的私有选择列表.

    Now the idea is to keep a ArrayChoiceList as private property within the choice loader. On build form loadValuesForChoices(...) is called, here we add all preset choices into our choice list so they can be displayed to the user. On build view loadChoiceList(...) is called, but we don't load anything, we just return our private choice list created before.

    现在用户与表单交互,一些额外的选择通过自动完成加载并放入 th HTML.在提交表单时,选定的值被提交,在我们的控制器操作中,首先创建表单,然后在 $form->handleRequest(..) loadChoicesForValues(...) 被调用,但提交的值可能与开头包含的值完全不同.因此,我们将内部选择列表替换为仅包含提交值的新列表.

    Now the user interacts with the form, some additional choices are loaded via an autocomplete and put into th HTML. On submit of the form the selected values are submitted and in our controller action first the form is created and afterwards on $form->handleRequest(..) loadChoicesForValues(...) is called, but the submitted values might be completly different from those which where included in the beginning. So we replace our internal choice list with a new one containing only the submitted values.

    我们的表单现在完美地保存了自动完成添加的数据.

    Our form now perfectly holds the data added by autocompletion.

    棘手的部分是,每当我们使用表单类型时,我们都需要一个新的选择加载器实例,否则内部选择列表将包含所有选择.

    The tricky part is, that we need a new instance of our choice loader whenever we use the form type, otherwise the internal choice list would hold a mixture of all choices.

    由于目标是编写一个新的自动完成选择类型,您通常会使用依赖注入将您的选择加载器传递到类型服务中.但是对于类型,如果您总是需要一个新实例,这是不可能的,相反我们必须通过选项包含它.在默认选项中设置选择加载器不起作用,因为它们也被缓存.要解决该问题,您必须编写一个匿名函数,该函数需要将选项作为参数:

    Since the goal is to write a new autocomplete choice type, you usually would use dependency injection to pass your choice loader into the type service.But for types this is not possible if you always need a new instance, instead we have to include it via options. Setting the choice loader in the default options does not work, since they are cached too. To solve that problem you have to write a anonymous function which needs to take the options as parameters:

    $resolver->setDefaults(array(
        'choice_loader' => function (Options $options) {
            return AutocompleteFactory::createChoiceLoader();
        },
    ));
    

    这是选择加载器类的简化版本:

    Here is a reduced version of the choice loader class:

    use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
    use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
    use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
    
    class AutocompleteChoiceLoader implements ChoiceLoaderInterface
    {
        /** @var ChoiceListInterface */
        private $choiceList;
    
        public function loadValuesForChoices(array $choices, $value = null)
        {
            // is called on form creat with $choices containing the preset of the bound entity
            $values = array();
            foreach ($choices as $key => $choice) {
                // we use a DataTransformer, thus only plain values arrive as choices which can be used directly as value
                if (is_callable($value)) {
                    $values[$key] = (string)call_user_func($value, $choice, $key);
                }
                else {
                    $values[$key] = $choice;
                }
            }
    
            // this has to be done by yourself:  array( label => value )
            $labeledValues = MyLabelService::getLabels($values);
    
            // create internal choice list from loaded values
            $this->choiceList = new ArrayChoiceList($labeledValues, $value);
    
            return $values;
        }
    
    
        public function loadChoiceList($value = null)
        {
            // is called on form view create after loadValuesForChoices of form create
            if ($this->choiceList instanceof ChoiceListInterface) {
                return $this->choiceList;
            }
    
            // if no values preset yet return empty list
            $this->choiceList = new ArrayChoiceList(array(), $value);
    
            return $this->choiceList;
        }
    
    
        public function loadChoicesForValues(array $values, $value = null)
        {
            // is called on form submit after loadValuesForChoices of form create and loadChoiceList of form view create
            $choices = array();
            foreach ($values as $key => $val) {
                // we use a DataTransformer, thus only plain values arrive as choices which can be used directly as value
                if (is_callable($value)) {
                    $choices[$key] = (string)call_user_func($value, $val, $key);
                }
                else {
                    $choices[$key] = $val;
                }
            }
    
            // this has to be done by yourself:  array( label => value )
            $labeledValues = MyLabelService::getLabels($values);
    
            // reset internal choice list
            $this->choiceList = new ArrayChoiceList($labeledValues, $value);
    
            return $choices;
        }
    }
    

    这篇关于Symfony 2.8 动态 ChoiceType 选项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-23 07:12
查看更多