如何在另一个选择框上使用相关的选择框?

Posted

技术标签:

【中文标题】如何在另一个选择框上使用相关的选择框?【英文标题】:How to use select box related on another select box? 【发布时间】:2012-02-21 14:24:39 【问题描述】:

如何在 Symfony 中使用相关的选择框?

假设我有一个包含公司的选择列表和另一个包含所选公司的员工的列表。我如何在 Symfony 中定义它们? 我已经创建了所有与 javascript 相关的代码,但是当提交我的表单并在某些字段上出现错误时,所有“子”选择字段都被重置为 null。

有什么想法吗? 谢谢,

编辑:由于这个问题似乎被误解了,我将添加一些精确度:

说明:

    我有一个实体 Company,其中包含使用 @OneToMany 关系的员工列表。 当我在选择/下拉列表中选择公司时,包含员工的第二个下拉列表通过 jQuery 更新。 该部分已完成,完美运行 提交表单时没有错误,实体表单解决方案工作正常。 提交包含错误的表单时,第二个下拉列表包含所有可能的值。它们不会在选定的公司中过滤。

尝试过的解决方案:

我的第一个想法是使用 form entity 类型,认为组件可以以某种方式绑定到另一个字段。 IE。根据所选公司的价值更新员工名单。

不工作,没有开箱即用的方法来做到这一点。甚至非开箱即用的解决方案...

然后我想到了手动将选定的公司作为参数传递给第二个下拉列表的查询生成器。

但是当创建表单时,值是空的。这些值仅在 bindRequest 上设置。

考虑过使用选择类型。通过 Javascript 将所有过滤器功能委托给 UI。这意味着当页面加载时,会出现一个空列表,并由基于所选公司的 Javascript 填充。

这确实有效,但我认为这里除了真正非常丑陋的编程之外没有别的词了。

PS:

已在此处、Symfony2 邮件列表、Twitter 和官方 Symfony 2 论坛上提出问题。当然,在发布我的问题之前,我已经多次搜索了其中的每一个。

【问题讨论】:

可以分享一下代码吗? 实际上没有多少代码可以分享。但尽我所能。 一开始你是通过jQuery选择某家公司的员工吗?因此,在提交有错误的表单后,此 jquery 代码不起作用,您会看到所有员工。检查 js 错误的 firebug 控制台或 chrome deleveloper 工具控制台。 【参考方案1】:

关于你已经尝试过的,我认为你应该重试你的第一个/第二个想法:

我的第一个想法是使用表单实体类型,考虑组件 可以以某种方式绑定在另一个字段上。 IE。更新员工名单 基于所选公司的价值。

您可以使用entity 类型填充员工选择框。 您所要做的就是定义好的选项:

class FooType extends AbstractType

    public function buildForm(FormBuilder $builder, array $options)
    
        $builder
            ->add('employee', 'entity', array(
                'class' => 'Entity\Employee',
                'query_builder' => function ($repository) use($options) 
                    return $repository
                        ->createQueryBuilder('e')
                        ->where('e.company = :company')
                        ->setParameter('company', $options['companyId'])
                    ;
                ,
            ))
        ;
    

    public function getDefaultOptions(array $options)
    
        return array('data_class' => 'Entity\Foo', 'companyId' => null);
    

然后我考虑手动将所选公司传递为 第二个下拉列表的查询生成器的参数。

此处的示例根据 companyId 表单的选项过滤员工列表。 您可以通过直接过滤表单数据中的公司来修改此行为。

public function buildForm(FormBuilder $builder, array $options)

    $companyId = $builder->getData()->getCompanyId();
    $builder
        ->add('employee', 'entity', array(
            'class' => 'Entity\Employee',
            'query_builder' => function ($repository) use ($companyId) 
                return $repository
                    ->createQueryBuilder('e')
                    ->where('e.company = :company')
                    ->setParameter('company', $companyId)
                ;
            ,
        ))
    ;

您仍然需要在 Entity\Foo 类中实现 getEmployee()setEmployee() 方法。

但是当创建表单时,值是空的。这些值仅设置在 绑定请求。

没有。使用表单工厂(第三个参数)创建表单时设置值, 或者当您致电$form->setData($foo); 时。当您bind 向表单输入新内容时,数据会被修改。

这种方法可能存在问题:绑定到表单的员工 ID 可能在表单选择列表中不可用,因为您更改了公司(因此也更改了员工)。

【讨论】:

非常感谢弗洛里安 :) 这对我有帮助,你甚至无法相信!!!【参考方案2】:

我遇到了同样的问题。您必须使用表单事件。我的代码示例与国家、地区、城市的关系。

namespace Orfos\UserBundle\Form\Type;

///import form events namespace
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Event\DataEvent;

class RegistrationFormType extends BaseType


    private $request;

    public function __construct($class, $request, $doctrine)
    
        parent::__construct($class);
        $this->request = $request;
        $this->doctrine = $doctrine;
    

    public function buildForm(FormBuilder $builder, array $options)
    
        parent::buildForm($builder, $options);
        //other fields

        $locale = $this->request->getLocale();

        $builder->add('country', 'entity', array(
            'class' => 'Orfos\CoreBundle\Entity\Country',
            'property' => $locale . 'name',
            'label' => 'register.country.label',
            'query_builder' => function(EntityRepository $er) 
                return $er->createQueryBuilder('c')
                                ->select('c', 't')
                                ->join('c.translations', 't');
            ,
        ));

        $factory = $builder->getFormFactory();
        $refreshRegion = function ($form, $country) use ($factory, $locale) 
                    $form->add($factory->createNamed('entity', 'region', null, array(
                                'class' => 'Orfos\CoreBundle\Entity\Region',
                                'property' => $locale . 'name',
                                'label' => 'register.region.label',
                                'query_builder' => function (EntityRepository $repository) use ($country) 
                                    $qb = $repository->createQueryBuilder('region')
                                            ->select('region', 'translation')
                                            ->innerJoin('region.country', 'country')
                                            ->join('region.translations', 'translation');

                                    if ($country instanceof Country) 
                                        $qb = $qb->where('region.country = :country')
                                                ->setParameter('country', $country);
                                     elseif (is_numeric($country)) 
                                        $qb = $qb->where('country.id = :country_id')
                                                ->setParameter('country_id', $country);
                                     else 
                                        $qb = $qb->where('country.id = 1');
                                    

                                    return $qb;
                                
                            )));
                ;
        $factory = $builder->getFormFactory();
        $refreshCity = function($form, $region) use ($factory, $locale) 
                    $form->add($factory->createNamed('entity', 'city', null, array(
                                'class' => 'Orfos\CoreBundle\Entity\City',
                                'property' => $locale . 'name',
                                'label' => 'register.city.label',
                                'query_builder' => function (EntityRepository $repository) use ($region) 
                                    $qb = $repository->createQueryBuilder('city')
                                            ->select('city', 'translation')
                                            ->innerJoin('city.region', 'region')
                                            ->innerJoin('city.translations', 'translation');

                                    if ($region instanceof Region) 
                                        $qb = $qb->where('city.region = :region')
                                                ->setParameter('region', $region);
                                     elseif (is_numeric($region)) 
                                        $qb = $qb->where('region.id = :region_id')
                                                ->setParameter('region_id', $region);
                                     else 
                                        $qb = $qb->where('region.id = 1');
                                    

                                    return $qb;
                                
                            )));
                ;

        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (DataEvent $event) use ($refreshRegion, $refreshCity) 
                    $form = $event->getForm();
                    $data = $event->getData();

                    if ($data == null)
                        $refreshRegion($form, null);
                        $refreshCity($form, null);
                    

                    if ($data instanceof Country) 
                        $refreshRegion($form, $data->getCountry()->getRegions());
                        $refreshCity($form, $data->getRegion()->getCities());
                    
                );

        $builder->addEventListener(FormEvents::PRE_BIND, function (DataEvent $event) use ($refreshRegion, $refreshCity) 
                    $form = $event->getForm();
                    $data = $event->getData();

                    if (array_key_exists('country', $data)) 
                        $refreshRegion($form, $data['country']);
                    
                    if (array_key_exists('region', $data)) 
                        $refreshCity($form, $data['region']);
                                        
                );
    


【讨论】:

感谢您的解决方案,我可以根据您的想法创建一个通用类型来创建链接下拉列表。 你好 Pivasyk。您对这篇文章的意见非常有趣。谢谢你。请问国家、地区和城市是设置在一个实体中还是三个不同的实体链接在一起? @Marc,嗯...我不明白你的问题...这是 3 个不同的实体,有一对多的关系,第 4 个实体是与城市、地区和国家相关联的用户【参考方案3】:

您可以创建操作,该操作将返回包含公司员工的 json 数组。 当公司列表发生变化时,您必须通过 ajax 请求此操作并创建选择的员工列表。

表单控制器操作:

/**
 * @Route("/job", name="_demo_job")
 * @Template()
 */
public function jobAction()

    $form = $this->createForm(new JobType());

    $request = $this->getRequest();
    if ($request->getMethod() == 'POST') 
        $form->bindRequest($request);
        $data = $form->getData();
        // some operations with form data
    

    return array(
        'form' => $form->createView(),
    );

Conroller,以 json 格式按公司返回员工:

/**
 * Finds all employees by company
 *
 * @Route("/id/show", name="employees_by_category")
 */
public function listByCompanyAction($id)

    $request = $this->getRequest();

    if ($request->isXmlHttpRequest() && $request->getMethod() == 'POST') 
        $em = $this->getDoctrine()->getEntityManager();

        $company = $em->getRepository('AcmeDemoBundle:Company')->find($id);

        // create array for json response
        $empoloyees = array();
        foreach ($company->getEmployees() as $employee) 
            $empoloyees[] = array($employee->getId(), $employee->getName());
        

        $response = new Response(json_encode($empoloyees));
        $response->headers->set('Content-Type', 'application/json');

        return $response;
    
    return new Response();

表单模板:

<script type="text/javascript">
    $(document).ready(function() 
        $('#form_company').change(function() 
            var companyId = $(this).val();
            $.post(' route /' + companyId + '/show', function(data) 
                // last selected employee
                var selectedVal = $('option:selected', '#form_employee').attr('value');

                $('#form_employee option').remove();
                for (i in data) 
                    // create option with employee
                    var option = $('<option></option>').
                        attr('value', data[i][0]).
                        text(data[i][1]);
                    // set selected employee
                    if (data[i][0] == selectedVal) 
                        option.attr('selected', 'selected');
                    
                    // append to employee to employees select
                    $('#form_employee').append(option);
                
            , 'json');
        )

        // request employees by company
        $('#form_company').change();
    )

</script>
<form action=" path('_demo_job') " method="post"  form_enctype(form) >
     form_widget(form) 

    <input type="submit" />
</form>

对于更详细的示例,您应该更详细地描述您的问题。

【讨论】:

感谢您的回答和努力,但您可能已经注意到,在我的问题中,我明确指出所有 jQuery json 部分都已实现。 在我的变体中,选择不会重置。您必须分享您的 js 和表单生成器操作,我尝试找到问题。

以上是关于如何在另一个选择框上使用相关的选择框?的主要内容,如果未能解决你的问题,请参考以下文章

选择列表框项目时,在另一个列表框中显示相关数据

单击组合框上的类别时如何选择文本? (Vue.JS 2)

如何在对话框内的选择框上应用更改事件

弹出框控件在选择不同的表视图行时更改其位置

使用带有键 -> 列名的 Map 在数据框上触发选择列

在选择框上至少有一个字符之前不要显示下拉菜单