簡體   English   中英

如何制作(最佳方式)特定的自定義Symfony 2表單元素類型?

[英]How to make (best way) specific custom Symfony 2 form element type?

我需要使用以下邏輯創建表單元素:

  • 用戶需要選擇他的預算
  • 他可以用單選按鈕選擇它
  • 或選擇“其他”並手動輸入

這是表示此邏輯的HTML標記:

Choose your budget:
<div id="briefing_budget" class="budget clearfix">
<label>
    <input type="radio" id="briefing_budget_0" name="briefing[budget][selected]" required="required" value="9999"> 9 999 rubles
</label>
<label>
    <input type="radio" name="briefing[budget][selected]" value="other"> other <input type="text" name="briefing[budget][number]">
</label>
</div>

為了完成這項工作,我使用自定義Twig塊創建了自定義字段類型。 Finnaly,我有一些我不喜歡的東西......

這是自定義類型的代碼:

<?php

namespace Company\Optimal\PromoAction\FreeCampaignFor10k;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList;
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToBooleanArrayTransformer;
use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener;
use Symfony\Component\Form\Extension\Core\View\ChoiceView;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;


/**
 * choices : [{"<value>": "<label>"}]
 */
class NumberRadioType extends AbstractType
{

    /**
     * Caches created choice lists.
     * @var array
     */
    private $choiceListCache = array();

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        if (!$options['choice_list'] && !is_array($options['choices']) && !$options['choices'] instanceof \Traversable) {
            throw new LogicException('Either the option "choices" or "choice_list" must be set.');
        }

        $preferredViews = $options['choice_list']->getPreferredViews();
        $remainingViews = $options['choice_list']->getRemainingViews();

        if (null !== $options['empty_value'] && 0 === count($options['choice_list']->getChoicesForValues(array('')))) {
            $placeholderView = new ChoiceView(null, '', $options['empty_value']);

            $this->addSubForms($builder, array('placeholder' => $placeholderView), $options);
        }

        $this->addSubForms($builder, $preferredViews, $options);
        $this->addSubForms($builder, $remainingViews, $options);


        $builder->addViewTransformer(new ChoiceToBooleanArrayTransformer($options['choice_list'], $builder->has('placeholder')));
        $builder->addEventSubscriber(new FixRadioInputListener($options['choice_list'], $builder->has('placeholder')), 10);

        $name = $builder->getName();
        $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($name) {
            $data = $event->getData();
            $data = $data['selected'] == 'other' ? $data['number'] : $data['selected'];
            $event->setData($data);
        });

    }


    /**
     * {@inheritdoc}
     */
    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $view->vars = array_replace($view->vars, array(
            'preferred_choices' => $options['choice_list']->getPreferredViews(),
            'choices' => $options['choice_list']->getRemainingViews(),
            'separator' => '-------------------',
            'empty_value' => null,
        ));

        $view->vars['is_selected'] = function ($choice, $value) {
            return $choice === $value;
        };

        $view->vars['empty_value_in_choices'] = 0 !== count($options['choice_list']->getChoicesForValues(array('')));

        if (null !== $options['empty_value'] && !$view->vars['empty_value_in_choices']) {
            $view->vars['empty_value'] = $options['empty_value'];
        }


    }

    public function finishView(FormView $view, FormInterface $form, array $options)
    {
        foreach ($view as $childView) {
            $childView->vars['full_name'] = $view->vars['full_name'] . '[selected]';
        }
    }


    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $choiceListCache =& $this->choiceListCache;

        $choiceList = function (Options $options) use (&$choiceListCache) {
            $choices = null !== $options['choices'] ? $options['choices'] : array();

            // Reuse existing choice lists in order to increase performance
            $hash = hash('sha256', json_encode(array($choices, $options['preferred_choices'])));

            if (!isset($choiceListCache[$hash])) {
                $choiceListCache[$hash] = new SimpleChoiceList($choices, $options['preferred_choices']);
            }

            return $choiceListCache[$hash];
        };

        $emptyData = array();

        $emptyValue = function (Options $options) {
            return $options['required'] ? null : '';
        };

        $emptyValueNormalizer = function (Options $options, $emptyValue) {
            if (false === $emptyValue) {
                return;
            } elseif ('' === $emptyValue) {
                return 'None';
            }

            return $emptyValue;
        };

        $resolver->setDefaults(array(
            'choice_list' => $choiceList,
            'choices' => array(),
            'preferred_choices' => array(),
            'empty_data' => $emptyData,
            'empty_value' => $emptyValue,
            'error_bubbling' => false,
            'compound' => true,
            'data_class' => null,
        ));

        $resolver->setNormalizers(array(
            'empty_value' => $emptyValueNormalizer,
        ));

        $resolver->setAllowedTypes(array(
            'choice_list' => array('null', 'Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface'),
        ));
    }


    /**
     * Returns the name of this type.
     *
     * @return string The name of this type
     */
    public function getName()
    {
        return 'number_radio';
    }

    /**
     * Adds the sub fields for an expanded choice field.
     *
     * @param FormBuilderInterface $builder The form builder.
     * @param array $choiceViews The choice view objects.
     * @param array $options The build options.
     */
    private function addSubForms(FormBuilderInterface $builder, array $choiceViews, array $options)
    {
        foreach ($choiceViews as $i => $choiceView) {
            if (is_array($choiceView)) {
                // Flatten groups
                $this->addSubForms($builder, $choiceView, $options);
            } else {
                $choiceOpts = array(
                    'value' => $choiceView->value,
                    'label' => $choiceView->label,
                    'translation_domain' => $options['translation_domain'],
                );


                $choiceType = 'radio';


                $builder->add($i, $choiceType, $choiceOpts);
            }
        }
    }
}

和模板:

{% block number_radio_widget %}
    {% spaceless %}
            <div {{ block('widget_container_attributes') }}>
                {% for child in form %}
                        <label>{{ form_widget(child) }}{{ child.vars.label }}</label>
                {% endfor %}
                <label><input type="radio" name="{{ form.vars.full_name }}[selected]" value="other"/>
                    other <input type="text" name="{{ form.vars.full_name }}[number]"/>
                </label>
            </div>
    {% endspaceless %}
{% endblock %}

我是Symfony的新手,所以我從Symfony的類ChoiceType復制了很多ChoiceType ,並且ChoiceType並不知道那里發生的一半東西的目的。 :)

Finnaly,問題是: 實現使用Symfony 2表單組件實現的目標的最佳(或至少更好)方法是什么?

如果你從“文本”中抄寫FormType,那么在twig中添加一些javascript會很簡單

設置表單類型中所需的最小值:

class NumberRadioType  extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->setAttribute('configs', $options['configs']);
    }

    /**
     * {@inheritdoc}
     */
    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $view->vars['configs'] = $form->getConfig()->getAttribute('configs');
    }

    /**
     * {@inheritdoc}
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'configs' => array()
        ));

        $resolver->setNormalizers(array(
            'configs' => function (Options $options, $value) {
                return true;
            }
        ));
    }


    /**
     * {@inheritdoc}
     */
    public function getParent()
    {
        return 'text';
    }

    /**
     * {@inheritdoc}
     */    
    public function getName()
    {
        return 'NumberRadio';
    }

在你做的html twig模板中:

{% block number_radio_widget %}
    {% spaceless %}
            <div {{ block('widget_container_attributes') }}>
                {% for child in form %}
                        <label>{{ form_widget(child) }}{{ child.vars.label }}</label>
                {% endfor %}
                <label><input type="radio" {{id}}/>
                    other <input type="text" name="{{ form.vars.full_name }}[number]" value="{{value}}"/>
                </label>
            </div>
    {% endspaceless %}
{% endblock %}

您可以使用{{id}}來獲取輸入的參數,例如名稱,ID和值

您可以使用{{value}}來獲取輸入的值

在javascript twig模板中,您可以自定義組件

{% block number_radio_javascript %}
    <script type="text/javascript">
  var selectedInput = $('input[name=briefing[budget][selected]]:checked', '#myForm').val()
if(selectedInput ==1){
//do something
}else{
//do something else
}

</script>
{% endblock %}

你需要添加一些代碼來包含javascript部分。 看看“genemu form”學習如何做,但基本上:你需要創建一個枝條擴展

https://github.com/genemu/GenemuFormBundle/blob/master/Twig/Extension/FormExtension.php

然后每次使用擴展名來使用你的formType

{% extends '::base.html.twig' %}
{% block jsscript %}
{{ vendor_type_javascript(form) }}
{% endblock %}
{% block content %}
{{ form_widget(edit_form) }}
        {{ form_errors(edit_form) }}
        {{ form_rest(edit_form) }}
{% endblock %}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM