2016-07-26 81 views
0

我有一個FormEvents的問題,我想動態填充3個字段。 我解釋說,我有3個字段:Project> Box> Cell,用戶選擇一個Project,Box列表被更新,他選擇一個Box,單元格列表被更新。動態生成帶表單事件的提交表單

要做到這一點,我用FormEvent像文件說(http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data

但是我有一個問題,只是一個場動態更新,它的工作,但沒有2場...其實用戶可以選擇一個項目,當他這樣做的時候,框中的字段會被更新。但是,當他選擇一個盒子時,單元格字段沒有更新...

但是,我找到了一些允許它工作的東西,只需在 - > add()和反轉到 - >加()。但我不想要它。

我的代碼是:

$builder 
    ->add('project', EntityType::class, array(
     'class' => 'AppBundle\Entity\Project', 
     'choice_label' => 'name', 
     'placeholder' => '-- select a project --', 
     'mapped' => false, 
    )) 
    ->add('box', EntityType::class, array(
     'class' => 'AppBundle\Entity\Box', 
     'choice_label' => 'name', 
     'placeholder' => '-- select a box --', 
     'choices' => [], 
    )) 
    ->add('cell', ChoiceType::class, array(
     'placeholder' => '-- select a cell --', 
    )) 
; 

當我將其更改爲:

builder 
    ->add('box', EntityType::class, array(
     'class' => 'AppBundle\Entity\Box', 
     'choice_label' => 'name', 
     'placeholder' => '-- select a box --', 
     // 'choices' => [], 
    )) 
    ->add('project', EntityType::class, array(
     'class' => 'AppBundle\Entity\Project', 
     'choice_label' => 'name', 
     'placeholder' => '-- select a project --', 
     'mapped' => false, 
    )) 

    ->add('cell', ChoiceType::class, array(
     'placeholder' => '-- select a cell --', 
    )) 
; 

它的工作......但我想在開始框中空列表,我想項目之前...

有點精確,這種形式作爲一個CollectionType被嵌入到另一種形式。

所有這種類型的代碼:

<?php 

namespace AppBundle\Form; 

use Symfony\Bridge\Doctrine\Form\Type\EntityType; 
use Symfony\Component\Form\AbstractType; 
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; 
use Symfony\Component\Form\FormBuilderInterface; 
use Symfony\Component\Form\FormEvent; 
use Symfony\Component\Form\FormEvents; 
use Symfony\Component\Form\FormInterface; 
use Symfony\Component\OptionsResolver\OptionsResolver; 

class TubeType extends AbstractType 
{ 
    /** 
    * @param FormBuilderInterface $builder 
    * @param array $options 
    */ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $builder 
      ->add('project', EntityType::class, array(
       'class' => 'AppBundle\Entity\Project', 
       'choice_label' => 'name', 
       'placeholder' => '-- select a project --', 
       'mapped' => false, 
      )) 
      ->add('box', EntityType::class, array(
       'class' => 'AppBundle\Entity\Box', 
       'choice_label' => 'name', 
       'placeholder' => '-- select a box --', 
       'choices' => [], 
      )) 
      ->add('cell', ChoiceType::class, array(
       'placeholder' => '-- select a cell --', 
      )) 
     ; 

     // MODIFIER 
     $boxModifier = function (FormInterface $form, $project) { 
      $boxes = (null === $project) ? [] : $project->getBoxes(); 

      $form->add('box', EntityType::class, array(
       'class' => 'AppBundle\Entity\Box', 
       'choice_label' => 'name', 
       'placeholder' => '-- select a box --', 
       'choices' => $boxes, 
      )); 
     }; 

     $cellModifier = function(FormInterface $form, $box) { 
      $cells = (null === $box) ? [] : $box->getEmptyCells(); 

      $form->add('cell', ChoiceType::class, array(
       'placeholder' => '-- select a cell --', 
       'choices' => $cells, 
      )); 
     }; 

     // FORM EVENT LISTENER 
     $builder->get('project')->addEventListener(
      FormEvents::POST_SUBMIT, 
      function(FormEvent $event) use ($boxModifier) { 
       $project = $event->getForm()->getData(); 

       $boxModifier($event->getForm()->getParent(), $project); 
      } 
     ); 

     $builder->get('box')->addEventListener(
      FormEvents::POST_SUBMIT, 
      function(FormEvent $event) use ($cellModifier) { 
       $box = $event->getForm()->getData(); 

       $cellModifier($event->getForm()->getParent(), $box); 
      } 
     ); 
    } 

    /** 
    * @param OptionsResolver $resolver 
    */ 
    public function configureOptions(OptionsResolver $resolver) 
    { 
     $resolver->setDefaults(array(
      'data_class' => 'AppBundle\Entity\Tube' 
     )); 
    } 
} 

非常感謝你的幫助:)

回答

0

您應該使用$builder->addEventListener。對於多個字段,您只需在FormEvents::PRE_SET_DATA事件處理程序中使用動態字段。此外,請使用父級字段數據,如doc中所述,以獲取子字段選項。

我已經使用這種方法來生成分層字段中的國家,州和城市實體。讓我知道它是否有幫助,或者您需要更多信息。

編輯:對於更大的邏輯,您可以使用eventSubscriber這將保持您的代碼乾淨,並且您還可以在其他地方重新使用表單的動態部分。

對於多個依賴分層字段,只需通過eventSubscriber類中的條件添加它們即可。

更新的代碼片段

這裏是代碼片段一個穿行在Symfony的2.7

注意工作對我來說:我不更換動態HTML字段描述該文檔,而不是通過jQuery,我只是收集子選項作爲每個選定的父項選項,並填寫這些。提交時,表格根據eventSubscriber上下文識別正確的子選項。因此,這裏是你將如何做到這一點:

在你父窗體類型(在這裏你擁有所有3場)調用eventSubscriber而不是定義這3個領域:

$builder->add(); // all other fields.. 
$builder->addEventSubscriber(new DynamicFieldsSubscriber()); 

創建eventSubscriber中的定義,文檔,這裏的文件名是DynamicFieldsSubscriber

<?php 
namespace YourBundle\Form\EventListener; 

use Symfony\Component\Form\FormEvent; 
use Symfony\Component\Form\FormEvents; 
use Symfony\Component\EventDispatcher\EventSubscriberInterface; 
use Doctrine\ORM\EntityRepository; 
use Symfony\Component\Form\FormInterface; 

class DynamicFieldsSubscriber implements EventSubscriberInterface 
{ 

    /** 
    * Define the events we need to subscribe 
    * @return type 
    */ 
    public static function getSubscribedEvents() 
    { 
     return array(
      FormEvents::PRE_SET_DATA => 'preSetData', // check preSetData method below 
      FormEvents::PRE_SUBMIT => 'preSubmitData', // check preSubmitData method below 
     ); 
    } 

    /** 
    * Handling form fields before form renders. 
    * @param FormEvent $event 
    */ 
    public function preSetData(FormEvent $event) 
    { 
     $location = $event->getData(); 
     // Location is the main entity which is obviously form's (data_class) 
     $form = $event->getForm(); 

     $country = ""; 
     $state = ""; 
     $district = ""; 

     if ($location) { 
      // collect preliminary values for 3 fields. 
      $country = $location->getCountry(); 
      $state = $location->getState(); 
      $district = $location->getDistrict(); 
     } 
     // Add country field as its static. 
     $form->add('country', 'entity', array(
      'class' => 'YourBundle:Country', 
      'label' => 'Select Country', 
      'empty_value' => ' -- Select Country -- ', 
      'required' => true, 
      'query_builder' => function (EntityRepository $er) { 
       return $er->createQueryBuilder('c') 
         ->where('c.status = ?1') 
         ->setParameter(1, 1); 
      } 
     )); 
     // Now add all child fields. 
     $this->addStateField($form, $country); 
     $this->addDistrictField($form, $state); 
    } 

    /** 
    * Handling Form fields before form submits. 
    * @param FormEvent $event 
    */ 
    public function preSubmitData(FormEvent $event) 
    { 
     $form = $event->getForm(); 
     $data = $event->getData(); 

     // Here $data will be in array format. 

     // Add property field if parent entity data is available. 
     $country = isset($data['country']) ? $data['country'] : $data['country']; 
     $state = isset($data['state']) ? $data['state'] : $data['state']; 
     $district = isset($data['district']) ? $data['district'] : $data['district']; 

     // Call methods to add child fields. 
     $this->addStateField($form, $country); 
     $this->addDistrictField($form, $state); 
    } 

    /** 
    * Method to Add State Field. (first dynamic field.) 
    * @param FormInterface $form 
    * @param type $country 
    */ 
    private function addStateField(FormInterface $form, $country = null) 
    { 
     $countryCode = (is_object($country)) ? $country->getCountryCode() : $country; 
     // $countryCode is dynamic here, collected from the event based data flow. 
     $form->add('state', 'entity', array(
      'class' => 'YourBundle:State', 
      'label' => 'Select State', 
      'empty_value' => ' -- Select State -- ', 
      'required' => true, 
      'attr' => array('class' => 'state'), 
      'query_builder' => function (EntityRepository $er) use($countryCode) { 
       return $er->createQueryBuilder('u') 
         ->where('u.countryCode = :countrycode') 
         ->setParameter('countrycode', $countryCode); 
      } 
     )); 
    } 

    /** 
    * Method to Add District Field, (second dynamic field) 
    * @param FormInterface $form 
    * @param type $state 
    */ 
    private function addDistrictField(FormInterface $form, $state = null) 
    { 
     $stateCode = (is_object($state)) ? $state->getStatecode() : $state; 
     // $stateCode is dynamic in here collected from event based data flow. 
     $form->add('district', 'entity', array(
      'class' => 'YourBundle:District', 
      'label' => 'Select District', 
      'empty_value' => ' -- Select District -- ', 
      'required' => true, 
      'attr' => array('class' => 'district'), 
      'query_builder' => function (EntityRepository $er) use($stateCode) { 
       return $er->createQueryBuilder('s') 
         ->where('s.stateCode = :statecode') 
         ->setParameter('statecode', $stateCode); 
      } 
     )); 
    } 
} 

在此之後,你需要寫jQuery events應在父選項的變化更新子選項明確地說,你不應該面對提交任何錯誤 表格。

注意:上面的代碼被提取並更改爲發佈在這裏。照顧namespace和有變數的地方。

+0

謝謝,但只是一個問題,當你在EventListener中生成字段時,你如何在這個上添加一個監聽器? 因爲當我在PRE_SET_DATA中添加字段時,我無法執行「$ builder-> get('project') - > addEventListener(」,因爲字段還不存在。:/ – mpiot

+0

而且如果我嘗試使用'builder-> addEventListener(FormEvents :: POST_SUBMIT,function(FormEvent $ event){',我沒有數據當我做'$ event-> getData();' – mpiot

+0

我已經更新了我的答案,通過文檔如果你仍然需要幫助,評論,我可以稍後添加一些代碼片段 – Jeet