如何使用Symfony表单动态填充下拉列表?

时间:2018-01-08 18:58:33

标签: json forms symfony

我想动态填写表格(见图片)

countries states and cities

状态select(html标记)由javascript函数填充json文件:

function onChangeCountries(countries, states) {
    $(countries).on("change", function(ev) {
        var countryId = $(this).val();

        if (countryId != '') {

            states.empty();
            states.append('<option selected="true" disabled>Choose state</option>');
            states.prop('selectedIndex', 0);

            $.getJSON(statesUrl, function (data) {
                $.each(data['states'], function (key, entry) {
                    if (entry.country_id == countryId) {
                        states.append($('<option></option>').attr('value', entry.id).text(entry.name));
                    }
                })
            });
        }
    });
}

国家和州的参数对应于两个第一select。 此函数填充与所选国家/地区对应的状态select。同样的机制适用于城市select

现在我想使用Symfony 4,这是我的表格:

<?php

// src/Form/LocationType.php
namespace App\Form;

use App\Entity\Location;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class LocationType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $countries = json_decode(file_get_contents("js/countries.json"), TRUE)['countries'];
        $countriesChoices = array();

        foreach($countries as $country) {
            $countriesChoices[$country['name']] = $country['id'];
        }

        $states = json_decode(file_get_contents("js/states.json"), TRUE)['states'];
        $statesChoices = array();

        foreach($states as $state) {
            $statesChoices[$state['name']] = $state['id'];
        }

        $cities = json_decode(file_get_contents("js/cities.json"), TRUE)['cities'];
        $citiesChoices = array();

        foreach($cities as $city) {
            $citiesChoices[$city['name']] = $city['id'];
        }

        $builder
            ->add('country', ChoiceType::class, array(
                'choices' => $countriesChoices
            ))
            ->add('area', ChoiceType::class, array(
                'choices'  => $statesChoices
            ))
            ->add('city', ChoiceType::class, array(
                'choices'  => array(
                    'Choose city' => $citiesChoices
            ))
            ->add('zip_code', TextType::class)
            ->add('street', TextType::class)
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => Location::class,
        ));
    }
}

这里的问题是我无法根据用户输入填写选项。 我也收到了OutOfMemoryException(cities.json中有超过40,000个城市)。

修改

在dlondero回答之后,我还没有解决我的问题,这是我的新LocationType课程:

<?php

// src/Form/LocationType.php
namespace App\Form;

use App\Entity\Location;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

class LocationType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $countries = json_decode(file_get_contents("js/countries.json"), TRUE)['countries'];
        $countriesChoices = array();

        foreach($countries as $country) {
            $countriesChoices[$country['name']] = $country['id'];
        }

        $builder
            ->add('zip_code', TextType::class)
            ->add('street', TextType::class)
            ->add('country', ChoiceType::class, array(
                'choices'  => $countriesChoices
            ))
            ->add('area', ChoiceType::class, array(
                'choices'  => array()
            ))
            ->add('city', ChoiceType::class, array(
                'choices'  => array()
            ))
        ;

        $formModifierStates = function (FormInterface $form, $countryId = null) {
            $statesChoices = array();

            if (null !== $countryId) {
                $states = json_decode(file_get_contents("js/states.json"), TRUE)['states'];

                foreach($states as $state) {
                    if ($countryId == $state['country_id']) {
                        $statesChoices[$state['name']] = $state['id'];
                    }
                }
            }

            $form->add('area', ChoiceType::class, array(
                'choices'  => $statesChoices
            ));
        };

        $formModifierCities = function (FormInterface $form, $stateId = null) {
            $citiesChoices = array();

            if (null !== $stateId) {
                $cities = json_decode(file_get_contents("js/cities.json"), TRUE)['cities'];

                foreach($cities as $city) {
                    if ($stateId == $city['state_id']) {
                        $citiesChoices[$city['name']] = $city['id'];
                    }
                }
            }

            $form->add('city', ChoiceType::class, array(
                'choices'  => $citiesChoices
            ));
        };

        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) use ($formModifierStates) {
                $data = $event->getData(); // country id

                $formModifierStates($event->getForm(), $data);
            }
        );

        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) use ($formModifierCities) {
                $data = $event->getData(); // state id

                $formModifierCities($event->getForm(), $data);
            }
        );

        $builder->get('country')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formModifierStates) {
                // It's important here to fetch $event->getForm()->getData(), as
                // $event->getData() will get you the client data (that is, the ID)
                $country = $event->getForm()->getData();

                // since we've added the listener to the child, we'll have to pass on
                // the parent to the callback functions!
                $formModifierStates($event->getForm()->getParent(), $country);
            }
        );

        $builder->get('area')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formModifierCities) {
                // It's important here to fetch $event->getForm()->getData(), as
                // $event->getData() will get you the client data (that is, the ID)
                $state = $event->getForm()->getData();

                // since we've added the listener to the child, we'll have to pass on
                // the parent to the callback functions!
                $formModifierCities($event->getForm()->getParent(), $state);
            }
        );
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => Location::class,
        ));
    }
}

填写表格后,我收到了消息:&#34;选择&#34; ...&#34;不存在或不是唯一的#34;。选择是指option中所选城市的ID(select的值)。

问题在于我无法弄清楚如何填充城市ChoiceType

1 个答案:

答案 0 :(得分:1)

您需要根据之前选择的选项动态加载它们。方法与JS很相似。

您可以在官方文档中看到一个示例:Dynamic Generation for Submitted Forms